560 lines
15 KiB
Lua
560 lines
15 KiB
Lua
local loveEncode,loveDecode=love.data.encode,love.data.decode
|
|
|
|
local WS=WS
|
|
local PLAYERS=PLAYERS
|
|
|
|
local NET={
|
|
allow_online=false,
|
|
accessToken=false,
|
|
cloudData={},
|
|
|
|
roomState={--A copy of room structure on server
|
|
roomInfo={
|
|
name=false,
|
|
type=false,
|
|
version=false,
|
|
},
|
|
roomData={},
|
|
count=false,
|
|
capacity=false,
|
|
private=false,
|
|
start=false,
|
|
},
|
|
spectate=false,--If player is spectating
|
|
specSRID=false,--Cached SRID when enter playing room, for connect WS after scene swapped
|
|
seed=false,
|
|
|
|
roomReadyState=false,
|
|
|
|
onlineCount="_",
|
|
}
|
|
|
|
local mesType={
|
|
Connect=true,
|
|
Self=true,
|
|
Broadcast=true,
|
|
Private=true,
|
|
Server=true,
|
|
}
|
|
|
|
--Parse json message
|
|
local function _parse(res)
|
|
res=JSON.decode(res)
|
|
if res then
|
|
if mesType[res.type]then
|
|
return res
|
|
else
|
|
MES.new('warn',("[%s] %s"):format(res.type or"?",res.reason or"[NO Message]"))
|
|
end
|
|
end
|
|
end
|
|
|
|
--WS close message
|
|
local function _closeMessage(message)
|
|
local mes=JSON.decode(message)
|
|
if mes then
|
|
MES.new('info',("%s %s|%s"):format(text.wsClose,mes.type or"",mes.reason or""))
|
|
else
|
|
MES.new('info',("%s %s"):format(text.wsClose,message))
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--------------------------<NEW API>
|
|
local function getMsg(request,timeout)
|
|
HTTP(request)
|
|
local totalTime=0
|
|
while true do
|
|
local mes=HTTP.pollMsg(request.pool)
|
|
if mes then
|
|
if type(mes.body)=='string' and #mes.body>0 then
|
|
return JSON.decode(mes.body)
|
|
else
|
|
MES.new('info',"Oops! Server is down")
|
|
return
|
|
end
|
|
else
|
|
totalTime=totalTime+coroutine.yield()
|
|
if totalTime>timeout then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function NET.getCode(email)
|
|
if not TASK.lock('getCode') then return end
|
|
TASK.new(function()
|
|
local res=getMsg({
|
|
pool='getCode',
|
|
path='/techmino/api/v1/auth/verify/email',
|
|
body={email=email},
|
|
},12.6)
|
|
|
|
if res then
|
|
if res.code==200 then
|
|
USER.email=email
|
|
SCN.fileDropped(2)
|
|
MES.new('info',"Please check your email",5)
|
|
else
|
|
MES.new('error',res.message,5)
|
|
end
|
|
else
|
|
MES.new('warn',"Request failed",5)
|
|
end
|
|
|
|
WAIT.interrupt()
|
|
end)
|
|
WAIT{
|
|
quit=function()
|
|
TASK.unlock('getCode')
|
|
HTTP.deletePool('getCode')
|
|
end,
|
|
timeout=12.6,
|
|
}
|
|
end
|
|
function NET.codeLogin(code)
|
|
if not TASK.lock('codeLogin') then return end
|
|
TASK.new(function()
|
|
local res=getMsg({
|
|
pool='codeLogin',
|
|
path='/techmino/api/v1/auth/login/email',
|
|
body={
|
|
email=USER.email,
|
|
code=code,
|
|
},
|
|
},6.26)
|
|
|
|
if res then
|
|
if res.code==200 then
|
|
USER.rToken=res.refreshToken
|
|
USER.aToken=res.accessToken
|
|
-- TODO: connect WS
|
|
SCN.pop()SCN.go('net_menu')
|
|
elseif res.code==201 then
|
|
USER.rToken=res.refreshToken
|
|
USER.aToken=res.accessToken
|
|
SCN.pop()SCN.push('net_menu')
|
|
SCN.fileDropped(3)
|
|
else
|
|
MES.new('error',res.message,5)
|
|
end
|
|
else
|
|
MES.new('warn',"Request failed",5)
|
|
end
|
|
|
|
WAIT.interrupt()
|
|
end)
|
|
WAIT{
|
|
quit=function()
|
|
TASK.unlock('codeLogin')
|
|
HTTP.deletePool('codeLogin')
|
|
end,
|
|
timeout=6.26,
|
|
}
|
|
end
|
|
function NET.setPW(code,pw)
|
|
if not TASK.lock('setPW') then return end
|
|
TASK.new(function()
|
|
pw=HASH.pbkdf2(HASH.sha3_256,pw,"salt",26000)
|
|
|
|
local res=getMsg({
|
|
pool='setPW',
|
|
method='PUT',
|
|
path='/techmino/api/v1/auth/reset/email',
|
|
body={
|
|
email=USER.email,
|
|
code=code,
|
|
newPassword=pw,
|
|
},
|
|
},6.26)
|
|
|
|
if res then
|
|
if res.code==200 then
|
|
USER.password=pw
|
|
MES.new('info',"Password set! Now you can login",5)
|
|
SCN.back()
|
|
else
|
|
MES.new('error',res.message,5)
|
|
end
|
|
else
|
|
MES.new('warn',"Request failed",5)
|
|
end
|
|
|
|
WAIT.interrupt()
|
|
end)
|
|
WAIT{
|
|
quit=function()
|
|
TASK.unlock('setPW')
|
|
HTTP.deletePool('setPW')
|
|
end,
|
|
timeout=6.26,
|
|
}
|
|
end
|
|
function NET.autoLogin()
|
|
if not USER.password then
|
|
SCN.go('login')
|
|
return
|
|
end
|
|
if not TASK.lock('autoLogin') then return end
|
|
TASK.new(function()
|
|
if USER.aToken then
|
|
local res=getMsg({
|
|
pool='autoLogin',
|
|
path='/techmino/api/v1/auth/check',
|
|
headers={["x-access-token"]=USER.aToken},
|
|
},6.26)
|
|
|
|
if res then
|
|
if res.code==200 then
|
|
-- TODO: connect WS
|
|
SCN.go('net_menu')
|
|
WAIT.interrupt()
|
|
return
|
|
else
|
|
MES.new('warn',res.message,5)
|
|
end
|
|
else
|
|
WAIT.interrupt()
|
|
return
|
|
end
|
|
end
|
|
if USER.rToken then
|
|
local res=getMsg({
|
|
pool='autoLogin',
|
|
path='/techmino/api/v1/auth/refresh',
|
|
headers={["x-refresh-token"]=USER.rToken},
|
|
},6.26)
|
|
|
|
if res then
|
|
if res.code==200 then
|
|
USER.rToken=res.refreshToken
|
|
USER.aToken=res.accessToken
|
|
-- TODO: connect WS
|
|
MES.new('info',"Login successed",5)
|
|
SCN.go('net_menu')
|
|
WAIT.interrupt()
|
|
return
|
|
else
|
|
MES.new('warn',res.message,5)
|
|
end
|
|
else
|
|
WAIT.interrupt()
|
|
return
|
|
end
|
|
end
|
|
if USER.password then
|
|
local res=getMsg({
|
|
pool='pwLogin',
|
|
path='/techmino/api/v1/auth/login/email',
|
|
body={
|
|
email=USER.email,
|
|
password=USER.password,
|
|
},
|
|
},6.26)
|
|
if res then
|
|
if res.code==200 then
|
|
USER.rToken=res.refreshToken
|
|
USER.aToken=res.accessToken
|
|
-- TODO: connect WS
|
|
MES.new('info',"Login successed",5)
|
|
SCN.go('net_menu')
|
|
WAIT.interrupt()
|
|
return
|
|
else
|
|
MES.new('warn',res.message,5)
|
|
end
|
|
else
|
|
WAIT.interrupt()
|
|
end
|
|
end
|
|
|
|
SCN.go('login')
|
|
WAIT.interrupt()
|
|
end)
|
|
WAIT{
|
|
quit=function()
|
|
TASK.unlock('autoLogin')
|
|
HTTP.deletePool('autoLogin')
|
|
end,
|
|
timeout=12.6,
|
|
}
|
|
end
|
|
function NET.pwLogin(email,pw)
|
|
if not TASK.lock('pwLogin') then return end
|
|
TASK.new(function()
|
|
pw=HASH.pbkdf2(HASH.sha3_256,pw,"salt",26000)
|
|
|
|
local res=getMsg({
|
|
pool='pwLogin',
|
|
path='/techmino/api/v1/auth/login/email',
|
|
body={
|
|
email=email,
|
|
password=pw,
|
|
},
|
|
},6.26)
|
|
|
|
if res then
|
|
if res.code==200 then
|
|
USER.email=email
|
|
USER.password=pw
|
|
USER.rToken=res.refreshToken
|
|
USER.aToken=res.accessToken
|
|
-- TODO: connect WS
|
|
SCN.go('net_menu')
|
|
else
|
|
MES.new('error',res.message,5)
|
|
end
|
|
else
|
|
MES.new('warn',"Request failed",5)
|
|
end
|
|
|
|
WAIT.interrupt()
|
|
end)
|
|
WAIT{
|
|
quit=function()
|
|
TASK.unlock('pwLogin')
|
|
HTTP.deletePool('pwLogin')
|
|
end,
|
|
timeout=12.6,
|
|
}
|
|
end
|
|
--------------------------</NEW API>
|
|
|
|
|
|
|
|
--Connect
|
|
function NET.wsconn()
|
|
if WS.status('game')=='dead'then
|
|
NET.roomState.start=true
|
|
WS.connect('stream','/stream',JSON.encode{
|
|
accessToken=USER.aToken,
|
|
},6)
|
|
TASK.new(NET.updateWS_stream)
|
|
end
|
|
end
|
|
|
|
--Disconnect
|
|
function NET.wsclose()
|
|
-- WS.close()
|
|
end
|
|
|
|
--Account & User
|
|
function NET.getUserInfo(uid)
|
|
WS.send('game',JSON.encode{
|
|
action=1,
|
|
data={
|
|
uid=uid,
|
|
hash=USERS.getHash(uid),
|
|
},
|
|
})
|
|
end
|
|
|
|
--Save
|
|
function NET.uploadSave()
|
|
if TASK.lock('uploadSave',8)then
|
|
WS.send('game',JSON.encode{action=2,data={sections={
|
|
{section=1,data=STRING.packTable(STAT)},
|
|
{section=2,data=STRING.packTable(RANKS)},
|
|
{section=3,data=STRING.packTable(SETTING)},
|
|
{section=4,data=STRING.packTable(KEY_MAP)},
|
|
{section=5,data=STRING.packTable(VK_ORG)},
|
|
{section=6,data=STRING.packTable(loadFile('conf/vkSave1','-canSkip')or{})},
|
|
{section=7,data=STRING.packTable(loadFile('conf/vkSave2','-canSkip')or{})},
|
|
}}})
|
|
MES.new('info',"Uploading")
|
|
end
|
|
end
|
|
function NET.downloadSave()
|
|
if TASK.lock('downloadSave',8)then
|
|
WS.send('game',JSON.encode{action=3,data={sections={1,2,3,4,5,6,7}}})
|
|
MES.new('info',"Downloading")
|
|
end
|
|
end
|
|
function NET.loadSavedData(sections)
|
|
for _,sec in next,sections do
|
|
if sec.section==1 then
|
|
NET.cloudData.STAT=STRING.unpackTable(sec.data)
|
|
elseif sec.section==2 then
|
|
NET.cloudData.RANKS=STRING.unpackTable(sec.data)
|
|
elseif sec.section==3 then
|
|
NET.cloudData.SETTING=STRING.unpackTable(sec.data)
|
|
elseif sec.section==4 then
|
|
NET.cloudData.keyMap=STRING.unpackTable(sec.data)
|
|
elseif sec.section==5 then
|
|
NET.cloudData.VK_org=STRING.unpackTable(sec.data)
|
|
elseif sec.section==6 then
|
|
NET.cloudData.vkSave1=STRING.unpackTable(sec.data)
|
|
elseif sec.section==7 then
|
|
NET.cloudData.vkSave2=STRING.unpackTable(sec.data)
|
|
end
|
|
end
|
|
local success=true
|
|
TABLE.cover(NET.cloudData.STAT,STAT)
|
|
success=success and saveStats()
|
|
|
|
TABLE.cover(NET.cloudData.RANKS,RANKS)
|
|
success=success and saveProgress()
|
|
|
|
TABLE.cover(NET.cloudData.SETTING,SETTING)
|
|
success=success and saveSettings()
|
|
applySettings()
|
|
|
|
TABLE.cover(NET.cloudData.keyMap,KEY_MAP)
|
|
success=success and saveFile(KEY_MAP,'conf/key')
|
|
|
|
TABLE.cover(NET.cloudData.VK_org,VK_ORG)
|
|
success=success and saveFile(VK_ORG,'conf/virtualkey')
|
|
|
|
if #NET.cloudData.vkSave1[1]then success=success and saveFile(NET.cloudData.vkSave1,'conf/vkSave1')end
|
|
if #NET.cloudData.vkSave2[1]then success=success and saveFile(NET.cloudData.vkSave2,'conf/vkSave2')end
|
|
if success then
|
|
MES.new('check',text.saveDone)
|
|
else
|
|
MES.new('warn',text.dataCorrupted)
|
|
end
|
|
end
|
|
|
|
--Room
|
|
function NET.fetchRoom()
|
|
if TASK.lock('fetchRoom',3)then
|
|
WS.send('game',JSON.encode{
|
|
action=0,
|
|
data={
|
|
type=nil,
|
|
begin=0,
|
|
count=10,
|
|
}
|
|
})
|
|
end
|
|
end
|
|
function NET.createRoom(roomName,description,capacity,roomType,roomData,password)
|
|
if TASK.lock('enterRoom',2)then
|
|
NET.roomState.private=not not password
|
|
NET.roomState.capacity=capacity
|
|
WS.send('game',JSON.encode{
|
|
action=1,
|
|
data={
|
|
capacity=capacity,
|
|
password=password,
|
|
roomInfo={
|
|
name=roomName,
|
|
type=roomType,
|
|
version=VERSION.room,
|
|
description=description,
|
|
},
|
|
roomData=roomData,
|
|
|
|
config=dumpBasicConfig(),
|
|
}
|
|
})
|
|
end
|
|
end
|
|
function NET.enterRoom(room,password)
|
|
if TASK.lock('enterRoom',6)then
|
|
SFX.play('reach',.6)
|
|
WS.send('game',JSON.encode{
|
|
action=2,
|
|
data={
|
|
rid=room.rid,
|
|
config=dumpBasicConfig(),
|
|
password=password,
|
|
}
|
|
})
|
|
end
|
|
end
|
|
|
|
--Play
|
|
function NET.checkPlayDisconn()
|
|
return WS.status('game')~='running'
|
|
end
|
|
function NET.signal_quit()
|
|
if TASK.lock('quit',3)then
|
|
WS.send('game',JSON.encode{action=3})
|
|
end
|
|
end
|
|
function NET.sendMessage(mes)
|
|
WS.send('game',JSON.encode{action=3,data={message=mes}})
|
|
end
|
|
function NET.changeConfig()
|
|
WS.send('game',JSON.encode{action=5,data={config=dumpBasicConfig()}})
|
|
end
|
|
function NET.signal_setMode(mode)
|
|
if not NET.roomState.start and TASK.lock('ready',3)then
|
|
WS.send('game',JSON.encode{action=6,data={mode=mode}})
|
|
end
|
|
end
|
|
function NET.signal_die()
|
|
WS.send('game',JSON.encode{action=4,data={score=0,survivalTime=0}})
|
|
end
|
|
function NET.uploadRecStream(stream)
|
|
WS.send('game',JSON.encode{action=5,data={stream=loveEncode('string','base64',stream)}})
|
|
end
|
|
|
|
--Chat
|
|
function NET.sendChatMes(mes)
|
|
WS.send('game',"T"..loveEncode('string','base64',mes))
|
|
end
|
|
function NET.quitChat()
|
|
WS.send('game','q')
|
|
end
|
|
|
|
--WS task funcs
|
|
function NET.freshPlayerCount()
|
|
while WS.status('game')=='running'do
|
|
TEST.yieldN(260)
|
|
if TASK.lock('freshPlayerCount',10)then
|
|
WS.send('game',JSON.encode{action=3})
|
|
end
|
|
end
|
|
end
|
|
function NET.updateWS_user()
|
|
while WS.status('game')~='dead'do
|
|
coroutine.yield()
|
|
local message,op=WS.read('user')
|
|
if message then
|
|
if op=='ping'then
|
|
elseif op=='pong'then
|
|
elseif op=='close'then
|
|
_closeMessage(message)
|
|
return
|
|
else
|
|
local res=_parse(message)
|
|
if res then
|
|
if res.type=='Connect'then
|
|
if res.uid then
|
|
USER.uid=res.uid
|
|
USER.authToken=res.authToken
|
|
if SCN.cur=='login'then
|
|
SCN.back()
|
|
end
|
|
end
|
|
MES.new('check',text.loginOK)
|
|
|
|
--Get self infos
|
|
NET.getUserInfo(USER.uid)
|
|
TASK.unlock('wsc_user')
|
|
elseif res.action==0 then--Get accessToken
|
|
NET.accessToken=res.accessToken
|
|
MES.new('check',text.accessOK)
|
|
NET.wsconn()
|
|
elseif res.action==1 then--Get userInfo
|
|
USERS.updateUserData(res.data)
|
|
elseif res.action==2 then--Upload successed
|
|
TASK.unlock('uploadSave')
|
|
MES.new('check',text.exportSuccess)
|
|
elseif res.action==3 then--Download successed
|
|
TASK.unlock('downloadSave')
|
|
NET.loadSavedData(res.data.sections)
|
|
MES.new('check',text.importSuccess)
|
|
end
|
|
else
|
|
WS.alert('user')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return NET
|