Files
Techmino/parts/net.lua
2021-04-27 21:41:01 +08:00

527 lines
12 KiB
Lua

local data=love.data
local ins,rem=table.insert,table.remove
local WS,TIME=WS,TIME
local NET={
connected=false,
allow_online=false,
allReady=false,
serverGaming=false,
roomList={},
accessToken=false,
rid=false,
rsid=false,
}
local mesType={
Connect=true,
Self=true,
Broadcast=true,
Private=true,
Server=true,
}
--Lock & Unlock submodule
local locks do
local rawset=rawset
locks=setmetatable({},{
__index=function(self,k)rawset(self,k,-1e99)return -1e99 end,
__newindex=function(self,k)rawset(self,k,-1e99)end,
})
end
function NET.lock(name,T)
if TIME()>=locks[name]then
locks[name]=TIME()+(T or 1e99)
return true
else
return false
end
end
function NET.unlock(name)
locks[name]=-1e99
end
function NET.getlock(name)
return TIME()<locks[name]
end
--Parse json message
local function _parse(res)
res=JSON.decode(res)
if res then
if mesType[res.type]then
return res
else
LOG.print(
"WS error:"..(
res.type and(
res.reason and res.type..": "..res.reason or
res.type
)or
"[NO Message]"
),
"warn")
end
end
end
--WS close message
local function wsCloseMessage(message)
local mes=JSON.decode(message)
if mes then
LOG.print(("%s [%s] %s"):format(text.wsClose,mes.type or"unknown type",mes.reason or""),"warn")
else
LOG.print(text.wsClose.."","warn")
end
end
--Connect
function NET.wsconn_app()
WS.connect("app","/app")
end
function NET.wsconn_user_pswd(email,password)
if NET.lock("wsc_user",5)then
WS.connect("user","/user",JSON.encode{
email=email,
password=password,
})
end
end
function NET.wsconn_user_token(uid,authToken)
if NET.lock("wsc_user",5)then
WS.connect("user","/user",JSON.encode{
uid=uid,
authToken=authToken,
})
end
end
function NET.wsconn_play()
if NET.lock("wsc_play",5)then
WS.connect("play","/play",JSON.encode{
uid=USER.uid,
accessToken=NET.accessToken,
})
end
end
function NET.wsconn_stream()
if NET.lock("wsc_stream",5)then
NET.serverGaming=true
WS.connect("stream","/stream",JSON.encode{
uid=USER.uid,
accessToken=NET.accessToken,
rid=NET.rsid,
})
TASK.new(NET.updateWS_stream)
end
end
--Disconnect
function NET.wsclose_app()WS.close("app")end
function NET.wsclose_user()WS.close("user")end
function NET.wsclose_play()WS.close("play")end
function NET.wsclose_stream()
NET.serverGaming=false
WS.close("stream")
end
--Account & User
function NET.register(username,email,password)
if NET.lock("register")then
WS.send("app",JSON.encode{
action=2,
data={
username=username,
email=email,
password=password,
}
})
end
end
function NET.pong(wsName,message)
WS.send(wsName,type(message)=="string"and message or"","pong")
end
function NET.getAccessToken()
if NET.lock("access_and_login",10)then
WS.send("user",JSON.encode{action=0})
end
end
function NET.getUserInfo(uid)
local hash=(not SETTING.dataSaving or nil)and USERS.getHash(uid)
WS.send("user",JSON.encode{
action=1,
data={
uid=uid,
hash=hash,
},
})
end
--Room
function NET.fetchRoom()
if NET.lock("fetchRoom",3)then
WS.send("play",JSON.encode{
action=0,
data={
type=nil,
begin=0,
count=10,
}
})
end
end
function NET.createRoom(roomType,name)
if NET.lock("enterRoom",1.26)then
WS.send("play",JSON.encode{
action=1,
data={
type=roomType,
name=name,
password=nil,
config=dumpBasicConfig(),
}
})
end
end
function NET.enterRoom(roomID,password)
if NET.lock("enterRoom",1.26)then
SFX.play("reach",.6)
NET.rid=roomID
WS.send("play",JSON.encode{
action=2,
data={
rid=roomID,
config=dumpBasicConfig(),
password=password,
}
})
end
end
--Play
function NET.checkPlayDisconn()
return WS.status("play")~="running"
end
function NET.signal_quit()
if NET.lock("quit",3)then
WS.send("play",'{"action":3}')
end
end
function NET.sendMessage(mes)
WS.send("play",'{"action":4,"data":{"message":'..mes..'}}')
end
function NET.changeConfig()
WS.send("play",'{"action":5,"data":'..JSON.encode({config=dumpBasicConfig()})..'}')
end
function NET.signal_ready(ready)
if NET.lock("ready",3)and not NET.serverGaming then
WS.send("play",'{"action":6,"data":{"ready":'..tostring(ready)..'}}')
end
end
function NET.signal_die()
WS.send("stream",'{"action":4,"data":{"score":0,"survivalTime":0}}')
end
function NET.uploadRecStream(stream)
WS.send("stream",'{"action":5,"data":{"stream":"'..data.encode("string","base64",stream)..'"}}')
end
--Chat
function NET.sendChatMes(mes)
WS.send("chat","T"..data.encode("string","base64",mes))
end
function NET.quitChat()
WS.send("chat","Q")
end
--WS tick funcs
function NET.updateWS_app()
while true do
YIELD()
if WS.status("app")=="running"then
local message,op=WS.read("app")
if message then
if op=="ping"then
NET.pong("app",message)
elseif op=="pong"then
elseif op=="close"then
wsCloseMessage(message)
return
else
local res=_parse(message)
if res then
if res.type=="Connect"then
NET.connected=true
if VERSION.code>=res.lowest then
NET.allow_online=true
if USER.authToken then
NET.wsconn_user_token(USER.uid,USER.authToken)
end
end
if VERSION.code<res.newestCode then
LOG.print(text.oldVersion:gsub("$1",res.newestName),180,COLOR.N)
end
LOG.print(res.notice,300,COLOR.N)
elseif res.action==0 then--Get new version info
--?
elseif res.action==1 then--Get notice
--?
elseif res.action==2 then--Register
if res.type=="Self"or res.type=="Server"then
LOG.print(res.data.message,300,COLOR.N)
if SCN.cur=="register"then
SCN.back()
end
else
LOG.print(res.reason or"Registration failed",300,COLOR.N)
end
NET.unlock("register")
end
else
WS.alert("app")
end
end
end
end
end
end
function NET.updateWS_user()
while true do
YIELD()
if WS.status("user")=="running"then
local message,op=WS.read("user")
if message then
if op=="ping"then
NET.pong("user",message)
elseif op=="pong"then
elseif op=="close"then
wsCloseMessage(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
FILE.save(USER,"conf/user","q")
if SCN.cur=="login"then SCN.back()end
end
LOG.print(text.loginSuccessed)
--Get self infos
NET.getUserInfo(USER.uid)
NET.unlock("wsc_user")
elseif res.action==0 then--Get accessToken
NET.accessToken=res.accessToken
LOG.print(text.accessSuccessed)
NET.wsconn_play()
elseif res.action==1 then--Get userInfo
USERS.updateUserData(res.data)
end
else
WS.alert("user")
end
end
end
end
end
end
function NET.updateWS_play()
while true do
YIELD()
if WS.status("play")=="running"then
local message,op=WS.read("play")
if message then
if op=="ping"then
NET.pong("play",message)
elseif op=="pong"then
elseif op=="close"then
wsCloseMessage(message)
return
else
local res=_parse(message)
if res then
local d=res.data
if res.type=="Connect"then
SCN.go("net_menu")
NET.unlock("wsc_play")
NET.unlock("access_and_login")
elseif res.action==0 then--Fetch rooms
NET.roomList=res.roomList
NET.unlock("fetchRoom")
elseif res.action==1 then--Create room (not used)
--?
elseif res.action==2 then--Player join
if res.type=="Self"then
--Create room
TABLE.clear(PLY_NET)
if d.players then
for _,p in next,d.players do
ins(PLY_NET,p.uid==USER.uid and 1 or #PLY_NET+1,{
uid=p.uid,
username=p.username,
sid=p.sid,
ready=p.ready,
config=p.config,
})
end
end
loadGame("netBattle",true,true)
else
--Load other players
ins(PLY_NET,{
uid=d.uid,
username=d.username,
sid=d.sid,
ready=d.ready,
config=d.config,
})
if SCN.socketRead then SCN.socketRead("Join",d)end
end
elseif res.action==3 then--Player leave
if not d.uid then
NET.wsclose_stream()
SCN.back()
NET.unlock("quit")
else
for i=1,#PLY_NET do
if PLY_NET[i].sid==d.sid then
rem(PLY_NET,i)
break
end
end
for i=1,#PLAYERS do
if PLAYERS[i].sid==d.sid then
rem(PLAYERS,i)
break
end
end
for i=1,#PLY_ALIVE do
if PLY_ALIVE[i].sid==d.sid then
rem(PLY_ALIVE,i)
break
end
end
if SCN.socketRead then SCN.socketRead("Leave",d)end
end
elseif res.action==4 then--Player talk
if SCN.socketRead then SCN.socketRead("Talk",d)end
elseif res.action==5 then--Player change settings
if tostring(USER.uid)~=d.uid then
for i=1,#PLY_NET do
if PLY_NET[i].uid==d.uid then
PLY_NET[i].config=d.config
PLY_NET[i].p:setConf(d.config)
break
end
end
resetGameData("qn")
end
elseif res.action==6 then--One ready
for i,p in next,PLY_NET do
if p.uid==d.uid then
if p.ready~=d.ready then
p.ready=d.ready
if not d.ready then NET.allReady=false end
SFX.play("spin_0",.6)
if i==1 then
NET.unlock("ready")
elseif not PLY_NET[1].ready then
for j=2,#PLY_NET do
if not PLY_NET[j].ready then
goto BREAK_notAllReady
end
end
SFX.play("blip_2",.5)
::BREAK_notAllReady::
end
end
break
end
end
elseif res.action==7 then--All Ready
SFX.play("reach",.6)
NET.allReady=true
elseif res.action==8 then--Set
NET.rsid=d.rid
NET.wsconn_stream()
elseif res.action==9 then--Game finished
NET.allReady=false
NET.wsclose_stream()
if SCN.socketRead then SCN.socketRead("Finish",d)end
end
else
WS.alert("play")
end
end
end
end
end
end
function NET.updateWS_stream()
while true do
YIELD()
if WS.status("stream")=="running"then
local message,op=WS.read("stream")
if message then
if op=="ping"then
NET.pong("stream",message)
elseif op=="pong"then
elseif op=="close"then
wsCloseMessage(message)
return
else
local res=_parse(message)
if res then
local d=res.data
if res.type=="Connect"then
NET.unlock("wsc_stream")
elseif res.action==0 then--Game start
SCN.socketRead("Go",d)
elseif res.action==1 then--Game finished
--?
elseif res.action==2 then--Player join
--?
elseif res.action==3 then--Player leave
--?
elseif res.action==4 then--Player died
for _,P in next,PLY_ALIVE do
if P.uid==d.uid then
P:lose(true)
break
end
end
elseif res.action==5 then--Receive stream
SCN.socketRead("Stream",d)
end
else
WS.alert("stream")
end
end
end
end
end
end
function NET.updateWS_chat()
while true do
YIELD()
if WS.status("chat")=="running"then
local message,op=WS.read("chat")
if message then
if op=="ping"then
NET.pong("chat",message)
elseif op=="pong"then
elseif op=="close"then
wsCloseMessage(message)
return
else
local res=_parse(message)
if res then
--TODO
else
WS.alert("chat")
end
end
end
end
end
end
return NET