Last active
July 7, 2016 15:45
-
-
Save JomerDev/9b94d0da7b912e2408fd3f6d9cc44464 to your computer and use it in GitHub Desktop.
File downloader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- most of this is not needed fo the download, but this code keeps the app running even when there is an error | |
-- when an error happens, this code offers the option to update the files through the server | |
-- start at line 200 if you only want the file download service | |
function run() | |
if love.math then | |
love.math.setRandomSeed(os.time()) | |
end | |
if love.load then love.load(arg) end | |
-- We don't want the first frame's dt to include time taken by love.load. | |
if love.timer then love.timer.step() end | |
local dt = 0 | |
-- Main loop time. | |
while true do | |
-- Process events. | |
if love.event then | |
love.event.pump() | |
for name, a,b,c,d,e,f in love.event.poll() do | |
if name == "quit" then | |
if not love.quit or not love.quit() then | |
return a | |
end | |
end | |
love.handlers[name](a,b,c,d,e,f) | |
end | |
end | |
-- Update dt, as we'll be passing it to update | |
if love.timer then | |
love.timer.step() | |
dt = love.timer.getDelta() | |
end | |
-- Call update and draw | |
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled | |
if love.graphics and love.graphics.isActive() then | |
love.graphics.clear(love.graphics.getBackgroundColor()) | |
love.graphics.origin() | |
if love.draw then love.draw() end | |
love.graphics.present() | |
end | |
if love.timer then love.timer.sleep(0.001) end | |
end | |
end | |
function love.errhand(msg) | |
msg = tostring(msg) | |
if msg:find("[QUIT]") then | |
return true | |
end | |
--local ip = "127.0.0.1" --"*" | |
local port = 6667 --6667 | |
if not love.window or not love.graphics or not love.event then | |
return true | |
end | |
if not love.graphics.isCreated() or not love.window.isOpen() then | |
local success, status = pcall(love.window.setMode, 800, 600) | |
if not success or not status then | |
return true | |
end | |
end | |
-- Reset state. | |
if love.mouse then | |
love.mouse.setVisible(true) | |
love.mouse.setGrabbed(false) | |
love.mouse.setRelativeMode(false) | |
end | |
if love.keyboard then | |
love.keyboard.setTextInput(false) | |
end | |
if love.joystick then | |
-- Stop all joystick vibrations. | |
for _,v in ipairs(love.joystick.getJoysticks()) do | |
v:setVibration() | |
end | |
end | |
if love.audio then love.audio.stop() end | |
love.graphics.reset() | |
local font = love.graphics.setNewFont(30) | |
love.graphics.setBackgroundColor(89, 157, 220) | |
love.graphics.setColor(255, 255, 255, 255) | |
local trace = debug.traceback() | |
love.graphics.clear(love.graphics.getBackgroundColor()) | |
love.graphics.origin() | |
local err = {} | |
table.insert(err, "Error\n") | |
table.insert(err, msg.."\n\n") | |
for l in string.gmatch(trace, "(.-)\n") do | |
if not string.match(l, "boot.lua") then | |
l = string.gsub(l, "stack traceback:", "Traceback\n") | |
table.insert(err, l) | |
end | |
end | |
local p = table.concat(err, "\n") | |
p = string.gsub(p, "\t", "") | |
p = string.gsub(p, "%[string \"(.-)\"%]", "%1") | |
local w,h = love.graphics.getDimensions() | |
local turn = false | |
if w > h then | |
love.graphics.rotate(math.rad(-90)) | |
love.graphics.translate(-h,0) | |
turn = true | |
w,h = h,w | |
end | |
local showUpdate = true | |
local update = false | |
local pos1_y = h-120 | |
local pos2_y = h-240 | |
local pos_x = 20 | |
local pos_w = w-40 | |
local pos_h = 100 | |
local retVal = true | |
local function draw() | |
local pos = 70 | |
love.graphics.setColor(255,255,255,255) | |
love.graphics.clear(love.graphics.getBackgroundColor()) | |
love.graphics.printf(tostring(p), pos, pos, love.graphics.getWidth() - pos) | |
love.graphics.setColor(200,0,0,255) | |
love.graphics.rectangle("fill",pos_x,pos1_y,pos_w,pos_h) | |
love.graphics.setColor(255,255,255,255) | |
local tw = font:getWidth("CLOSE") | |
local th = font:getHeight() | |
love.graphics.print("CLOSE", pos_x + (pos_w-tw)/2,pos1_y + (pos_h-th)/2) | |
if showUpdate then | |
love.graphics.setColor(192,192,192,255) | |
love.graphics.rectangle("fill",pos_x,pos2_y,pos_w,pos_h) | |
love.graphics.setColor(0,0,0,255) | |
local tw = font:getWidth("UPDATE") | |
local th = font:getHeight() | |
love.graphics.print("UPDATE", pos_x + (pos_w-tw)/2,pos2_y + (pos_h-th)/2) | |
end | |
love.graphics.present() | |
end | |
local updateState = 0 | |
local service; | |
local net = require("net") | |
net.close() | |
while true do | |
love.event.pump() | |
for e, a, b, c, d in love.event.poll() do | |
if e == "quit" then | |
return true | |
elseif e == "keypressed" and a == "escape" then | |
if not update then | |
return false | |
end | |
elseif e == "touchpressed" then | |
local handled = update and true or false | |
if turn then | |
b,c=c,b | |
end | |
if b > pos_x and b < pos_x + pos_w then | |
if c > pos1_y and c < pos1_y + pos_h then | |
-- Close | |
return true | |
elseif c > pos2_y and c < pos2_y + pos_h and showUpdate then | |
-- update | |
update = true | |
service = net.getDiscoverService() | |
local a,b,c = service(port,"TCPING","TCPONG",10) | |
if a == "Finished" then | |
net.setIp(b,c) | |
updateState = 2 | |
service = net.getTCPConnection() | |
end | |
updateState = 1 | |
handled = true | |
showUpdate = false | |
end | |
end | |
if not handled then | |
local name = love.window.getTitle() | |
if #name == 0 or name == "Untitled" then name = "Game" end | |
local buttons = {"OK", "Cancel"} | |
local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons) | |
if pressed == 1 then | |
return false | |
end | |
end | |
elseif e == "mousepressed" and not d then | |
love.event.push("touchpressed",1,a,b) | |
end | |
end | |
draw() | |
if update then | |
if updateState == 1 then | |
local a,b,c = service(love.timer.getDelta()) | |
p = "Discovering Server...\n Tries: "..tostring(b).."/"..tostring(c) | |
if a == "Finished" then | |
net.setIp(b,c) | |
updateState = 2 | |
service = net.getTCPConnection() | |
elseif not a then | |
error(b) | |
end | |
elseif updateState == 2 then | |
local state,err = service() | |
if state then | |
updateState = 3 | |
p = "Connected" | |
service = net.updateFiles() | |
elseif err then | |
error(err) | |
else | |
p = "Connecting to server..." | |
end | |
elseif updateState == 3 then | |
local state,a,b,c = pcall(service) | |
if state and a and b == "Finished" then | |
local function getAllFiles(path,data) | |
local data = data or {} | |
local files = love.filesystem.getDirectoryItems(path) | |
for _,v in pairs(files) do | |
if love.filesystem.isDirectory(path.."/"..tostring(v)) then | |
getAllFiles(path.."/"..tostring(v),data) | |
else | |
local str = (path.."/"..tostring(v)):gmatch(".+%.")() | |
str = str:gsub("/","."):sub(2,-2) | |
data[str] = true | |
end | |
end | |
return data | |
end | |
local state,files = pcall(getAllFiles,"/mobile") | |
for i in pairs(files) do | |
if package.loaded[i] then | |
package.loaded[i] = nil | |
end | |
end | |
retVal = false | |
break | |
end | |
p = table.concat({a,b,c},"\n") | |
end | |
end | |
if love.timer then | |
love.timer.sleep(0.1) | |
end | |
end | |
return retVal | |
end | |
function love.run() | |
while true do | |
local reload = false | |
if love.filesystem.isFile("mobile/init.lua") then | |
local state,err = pcall(require,"mobile.init") | |
if not state then | |
love.errhand(err) | |
end | |
else | |
love.errhand("No init file found. Please update...") | |
end | |
local state, br,err = xpcall(run,love.errhand) | |
if br == true then | |
break | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
io.stdout:setvbuf'no' | |
local socket = require("socket") | |
local udp = socket.udp() | |
udp:settimeout(0) | |
udp:setsockname("*",6667) | |
udp:setoption("broadcast",true) | |
local tcp = socket.tcp() | |
tcp:settimeout(0) | |
tcp:bind("*",6667) | |
tcp:listen() | |
tcp:setoption("reuseaddr",true) | |
local someRandomIP = "192.168.1.122" --This address you make up | |
local someRandomPort = "3102" --This port you make up | |
local mySocket = socket.udp() --Create a UDP socket like normal | |
--This is the weird part, we need to set the peer for some reason | |
mySocket:setpeername(someRandomIP,someRandomPort) | |
--I believe this binds the socket | |
--Then we can obtain the correct ip address and port | |
local myDevicesIpAddress, somePortChosenByTheOS = mySocket:getsockname()-- returns IP and Port | |
local clients = {} | |
local transfers = {} | |
local bytes = 4096 | |
local function getAllFiles(path) | |
local files = love.filesystem.getDirectoryItems(path) | |
local data = {} | |
for i,v in pairs(files) do | |
if love.filesystem.isDirectory(path.."/"..tostring(v)) then | |
data[v] = getAllFiles(path.."/"..tostring(v)) | |
else | |
local locPath = (path.."/"..tostring(v)):sub(8) | |
data[v] = {file = true,time = love.filesystem.getLastModified(path.."/"..tostring(v)),path = locPath} | |
end | |
end | |
return data | |
end | |
function updateClientData(client,id,bytes) | |
local client = client | |
client:settimeout(0) | |
local files = getAllFiles("/mobile") | |
local bytesPerMsg = bytes or 4096 | |
local id = id | |
local size = 0 | |
coroutine.yield(true) | |
local function loopFiles(tab) | |
for i,v in pairs(tab) do | |
if v.file then | |
client:send("TCFILE#"..tostring(v.path).."#"..tostring(v.time).."#\n") | |
local ans = "" | |
while true do | |
local msg = client:receive("*l") | |
if msg then | |
ans = msg | |
break | |
else | |
coroutine.yield(true) | |
end | |
end | |
if ans == "TCUPDATEFILE" then | |
local file = love.filesystem.newFile("/mobile"..v.path) | |
size = file:getSize() | |
client:send("TCSIZE#"..tostring(file:getSize()).."#"..tostring(bytesPerMsg).."#\n") | |
file:open("r") | |
client:settimeout(0.1) | |
while true do | |
local payload = file:read(bytesPerMsg) | |
file:seek(bytesPerMsg) | |
if payload then | |
size = size - bytesPerMsg | |
client:send(payload) | |
if size <= 0 then | |
break | |
end | |
coroutine.yield(true) | |
else | |
break | |
end | |
end | |
local ans = "" | |
while true do | |
local msg = client:receive("*l") | |
if msg then | |
ans = msg | |
break | |
else | |
coroutine.yield(true) | |
end | |
end | |
print(ans) | |
end | |
else | |
loopFiles(v) | |
end | |
end | |
end | |
loopFiles(files) | |
return true, id | |
end | |
function love.update(dt) | |
love.window.setTitle(tostring(love.timer.getFPS())) | |
local msg,addr,port = udp:receivefrom() | |
if msg then | |
print(msg,addr,port) | |
if msg == "TCPING" then | |
local s = "TCPONG" | |
udp:sendto(s,addr,port) | |
end | |
end | |
local client = tcp:accept() | |
if client then | |
print(client) | |
client:settimeout(0) | |
table.insert(clients,{socket = client, used = false}) | |
end | |
for i,v in pairs(clients) do | |
if not v.used then | |
local msg = v.socket:receive("*l") | |
if msg then | |
print(msg) | |
if msg == "TCUPDATEFILES" then | |
local coro = coroutine.wrap(updateClientData) | |
v.used = true | |
coro(v.socket, i) | |
table.insert(transfers,coro) | |
elseif msg == "TCDONE" then | |
elseif msg == "TCQUIT" then | |
for j,k in pairs(users) do | |
if k.client == i then | |
clients[i] = nil | |
end | |
end | |
else | |
local data = {} | |
for dat in tostring(msg):gmatch(".-#") do | |
table.insert(data,dat:sub(1,-2)) | |
end | |
if data[1] == "TCCONNECT" then | |
users[tostring(data[2])] = { | |
client = i | |
} | |
end | |
end | |
end | |
end | |
end | |
for i,v in pairs(transfers) do | |
local res,id = v() | |
if res and id then | |
clients[id].socket:send("TCRELOAD#\n") | |
transfers[i] = nil | |
clients[id].used = false | |
end | |
end | |
end | |
function love.draw() | |
love.graphics.setColor(255,255,255,255) | |
love.graphics.print(tostring(myDevicesIpAddress),10,10) | |
end | |
function love.quit() | |
udp:close() | |
tcp:close() | |
end | |
function love.quit() | |
udp:close() | |
tcp:close() | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- searches for server, gets servers ip and downloads all files | |
-- it tries to check which files to download, but that doesn't work right now | |
local net = {} | |
net.socket = require("socket") | |
function net.setIp(ip,port) | |
net.ip = ip | |
net.port = port | |
net.bytes = 4096 | |
end | |
function net.close() | |
if net.tcp and net.tcp.close then | |
pcall(net.tcp.send,net.tcp,"TCQUIT") | |
pcall(net.tcp.close,net.tcp) | |
end | |
if net.udp and net.udp.close then | |
pcall(net.udp.close,net.udp) | |
end | |
net.connected = false | |
end | |
function net.isConnected() | |
return net.connected | |
end | |
function net.getTCP() | |
return net.tcp | |
end | |
local function discoverServer(port,ping,pong,maxtries) | |
net.udp = net.socket.udp() | |
net.udp:settimeout(0) | |
net.udp:setoption("broadcast",true) | |
local timer = 5 | |
local port = port | |
local ping = ping or "PING" | |
local pong = pong or "PONG" | |
local dt = 0 | |
local tries = 0 | |
local maxtries = maxtries or 10 | |
while true do | |
timer = timer + dt | |
if timer >= 5 then | |
timer = timer - 5 | |
tries = tries + 1 | |
if tries >= maxtries then | |
net.udp:close() | |
return false,"Server not found" | |
end | |
local state, err = net.udp:sendto(ping,"255.255.255.255",port) | |
if not state then | |
return false, err | |
else | |
dt = coroutine.yield("working",tries,maxtries) | |
end | |
end | |
local msg, addr, por = net.udp:receivefrom() | |
if msg == pong then | |
net.udp:close() | |
return "Finished",addr,por | |
else | |
dt = coroutine.yield("working",tries,maxtries) | |
end | |
end | |
end | |
function net.getDiscoverService() | |
return coroutine.wrap(discoverServer) | |
end | |
function getTCP() | |
net.tcp = net.socket.tcp() | |
net.tcp:settimeout(1) | |
local state,err = net.tcp:connect(net.ip, net.port) | |
if not state then | |
return false, err | |
else | |
net.connected = true | |
return true | |
end | |
while true do | |
local data = socket.select({net.tcp}, {net.tcp} ,0.1) | |
if #data > 0 then | |
net.connected = true | |
return true | |
else | |
coroutine.yield(false) | |
end | |
end | |
end | |
function net.getTCPConnection() | |
return coroutine.wrap(getTCP) | |
end | |
function updateFiles(...) | |
net.tcp:settimeout(0.1) | |
local function getAllFiles(path) | |
local files = love.filesystem.getDirectoryItems(path) | |
local data = {} | |
for _,v in pairs(files) do | |
if love.filesystem.isDirectory(path.."/"..tostring(v)) then | |
data[v] = getAllFiles(path.."/"..tostring(v)) | |
else | |
data[v] = "FILE" | |
end | |
end | |
return data | |
end | |
local files = getAllFiles("/mobile") | |
net.tcp:send("TCUPDATEFILES\n") | |
local filebytes = 0 | |
local bytesPerMsg = 4096 | |
local receivedBytes = 0 | |
local fileTransfer = false | |
local filePath = "" | |
local file; | |
while true do | |
if not fileTransfer then | |
local msg = net.tcp:receive("*l") | |
if msg then | |
local data = {} | |
for dat in tostring(msg):gmatch(".-#") do | |
table.insert(data,dat:sub(1,-2)) | |
end | |
if data[1] == "TCFILE" then | |
local file = files | |
for path in data[2]:gmatch("/.-") do | |
path = path:sub(2) | |
if file[path] then | |
if file[path] == "FILE" then | |
local modtime, errormsg = love.filesystem.getLastModified(tostring(data[2]) ) | |
if errormsg then | |
net.tcp:send("TCUPDATEFILE\n") | |
filePath = tostring(data[2]) | |
break | |
else | |
if tostring(modtime) == data[3] then | |
net.tcp:send("TCNEXTFILE\n") | |
break | |
else | |
net.tcp:send("TCUPDATEFILE\n") | |
filePath = tostring(data[2]) | |
break | |
end | |
end | |
else | |
file = file[path] | |
end | |
else | |
net.tcp:send("TCUPDATEFILE\n") | |
filePath = tostring(data[2]) | |
break | |
end | |
end | |
elseif data[1] == "TCSIZE" then | |
filebytes = tonumber(data[2]) | |
bytesPerMsg = data[3] or bytesPerMsg | |
fileTransfer = true | |
if love.filesystem.exists("/mobile"..filePath) then | |
love.filesystem.remove("/mobile"..filePath) | |
end | |
local dirPath = filePath:gmatch(".+/")() | |
if dirPath then | |
love.filesystem.createDirectory("/mobile"..tostring(dirPath)) | |
end | |
file = love.filesystem.newFile("/mobile"..filePath) | |
file:open("w") | |
receivedBytes = 0 | |
elseif data[1] == "TCRELOAD" then | |
return true, "Finished" | |
end | |
end | |
else | |
if receivedBytes + bytesPerMsg > filebytes then | |
bytesPerMsg = filebytes-receivedBytes | |
end | |
local payload, err, partial = net.tcp:receive(bytesPerMsg) | |
if payload then | |
receivedBytes = receivedBytes + bytesPerMsg | |
file:write(payload) | |
if receivedBytes >= filebytes then | |
file:close() | |
net.tcp:send("TCDONE\n") | |
fileTransfer = false | |
end | |
end | |
end | |
coroutine.yield("working",fileTransfer and "Receiving file...\n"..tostring(filePath).."\n"..tostring(receivedBytes).." / "..tostring(filebytes).."bytes" or "Updating files\n"..tostring(filePath)) | |
end | |
end | |
function net.updateFiles() | |
return coroutine.wrap(updateFiles) | |
end | |
return net |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to use:
Put the main.lua for mobile and net.lua on your (android) phone and run it. It should display an error that it can't find any init.lua file.
Now start the servers main.lua on any device in the same network. create a folder mobile next to it and put any files for the mobile into it.
That should include an init.lua file, in which your main code (love.load, love.draw, etc) is.
when the server has started, just press the update button on the mobile phone and it 'should' try to find the server by broadcasting a ping.
As soon as the server is found it will copy all files in the mobile folder onto your phone.
When it is done it will try to restart, but that doesn't work yet.
! You need to restart love2d after updating this way ! The new files only show up after a restart