Files
Techmino/parts/net.lua

534 lines
11 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,
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
WS.connect("stream","/stream",JSON.encode{
uid=USER.uid,
accessToken=NET.accessToken,
rid=NET.rsid,
})
end
end
--Disconnect
function NET.wsclose_user()
WS.close("user")
end
function NET.wsclose_play()
WS.close("play")
end
function NET.wsclose_stream()
WS.close("stream")
end
--Account
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,message,"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(id,ifDetail)
WS.send("user",JSON.encode{
action=1,
data={
id=id or USER.uid,
detailed=ifDetail or false,
},
})
end
function NET.storeUserInfo(d)
local user=USERS[d.uid]
if not user then
user={}
USERS[d.uid]=user
end
user.uid=d.uid
user.username=d.username
user.motto=d.motto
user.avatar=d.avatar
--Get own name
if d.uid==USER.uid then
USER.username=d.username
FILE.save(USER,"conf/user","q")
end
-- FILE.save(USERS,"conf/users")
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
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_ready(ready)
if NET.lock("ready",3)then
WS.send("play",'{"action":6,"data":{"ready":'..tostring(ready)..'}}')
end
end
function NET.signal_quit()
if NET.lock("quit",3)then
WS.send("play",'{"action":3}')
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()
local retryTime=5
while true do
YIELD()
local status=WS.status("app")
if status=="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.sky)
end
LOG.print(res.notice,300,COLOR.sky)
elseif res.action==0 then--Get new version info
--?
elseif res.action==1 then--Get notice
--?
elseif res.action==2 then--Register
LOG.print(res.data.message,300,COLOR.sky)
if SCN.cur=="register"then SCN.back()end
NET.unlock("register")
end
else
WS.alert("app")
end
end
end
elseif status=="dead"then
retryTime=retryTime-1
if retryTime==0 then return end
for _=1,120 do YIELD()end
WS.connect("app","/app")
end
end
end
function NET.updateWS_user()
while true do
YIELD()
local status=WS.status("user")
if status=="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")
SCN.back()
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
NET.storeUserInfo(res.data)
end
else
WS.alert("user")
end
end
end
end
end
end
function NET.updateWS_play()
while true do
YIELD()
local status=WS.status("play")
if status=="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)
return
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
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
break
elseif j==#PLY_NET then
SFX.play("warning",.5)
end
end
end
end
break
end
end
elseif res.action==7 then--Ready
SFX.play("reach",.6)
elseif res.action==8 then--Set
NET.rsid=d.rid
NET.wsconn_stream()
TASK.new(NET.updateWS_stream)
elseif res.action==9 then--Game finished
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()
local status=WS.status("stream")
if status=="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
--?
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()
local status=WS.status("chat")
if status=="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