1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
local enet = require("enet")
local socket = require("socket")
local util = require("util")
local common = require("common")
local save_file = require("save_file")
local server = {}
local function migrate_save(save)
save.players = save.players or {}
end
local function save_data(srv)
-- TODO: handle failure
save_file.write(srv.save_file, srv.data)
end
local function get_player(srv, name)
for _, player in ipairs(srv.data.players) do
if player.name == name then
return player
end
end
end
local function get_players(srv)
local players = {}
for _, player in ipairs(srv.data.players) do
table.insert(players, {
name = player.name,
active = srv.players[player.name] ~= nil,
})
end
return players
end
local function get_info_pkt(srv)
return util.json_enc({
type = "client_info",
players = get_players(srv),
})
end
local function broadcast_info(srv)
local pkt = get_info_pkt(srv)
for _, clt in pairs(srv.clients) do
clt.peer:send(pkt)
end
end
local function create_player(srv, name)
local player = { name = name }
table.insert(srv.data.players, player)
print("[server] created player " .. name)
save_data(srv)
return player
end
local function disconnect(srv, clt)
srv.clients[clt.peer] = nil
if clt.player then
srv.players[clt.player.name] = nil
end
broadcast_info(srv)
end
local function select_player(srv, clt, pkt)
if #pkt.name > 128 then
return "name_too_long"
end
if srv.players[pkt.name] then
return nil, "already_active"
end
local player = get_player(srv, pkt.name)
if pkt.create and player then
return nil, "already_exists"
end
if not pkt.create and not player then
return nil, "not_exists"
end
if pkt.create then
player = create_player(srv, pkt.name)
end
return player
end
function server.create(filename, match_addr)
local srv = {}
srv.host = enet.host_create()
srv.secret = util.rand_string(common.secret_len)
srv.clients = {}
srv.players = {}
srv.match = srv.host:connect(match_addr or common.default_match_addr)
srv.match_req = socket.gettime()
local save, err = save_file.read(filename)
if err then
return nil, err
end
srv.save_file = filename
srv.data = save
migrate_save(save)
return srv
end
local function handle_match(srv, pkt)
if pkt.type == "server_match" then
local game_id = type(pkt.game_id) == "string" and util.base64_dec(pkt.game_id)
if not game_id then
print("[server] server_match: invalid game_id")
return
end
if srv.game_id then
print("[server] server_match: received while game already running")
return
end
srv.game_id = game_id
srv.invite = util.base64_enc(srv.game_id .. srv.secret)
elseif pkt.type == "server_join" then
if type(pkt.peer_addr) ~= "string" then
print("[server] server_join: invalid peer_addr")
return
end
srv.host:connect(pkt.peer_addr)
end
end
local function handle_client(srv, peer, pkt)
if pkt.type == "server_hi" then
local secret = type(pkt.secret) == "string" and util.base64_dec(pkt.secret)
if not secret then
print("[server] server_hi: invalid secret")
return
end
if srv.clients[peer] then
print("[server] server_hi: client already connected")
return
end
if secret == srv.secret then
print("[server] auth success " .. tostring(peer))
local clt = { peer = peer }
srv.clients[peer] = clt
util.send(peer, {
type = "client_hi",
})
peer:send(get_info_pkt(srv))
else
print("[server] auth failure " .. tostring(peer))
util.send(peer, { type = "client_reject" })
peer:disconnect_later()
end
end
local clt = srv.clients[peer]
if not clt then
print("[server] dropping unauthenicated packet from " .. tostring(peer))
return
end
if pkt.type == "server_player" then
if clt.player then
print("[server] dropping server_player from already authenticated player")
return
end
if type(pkt.name) ~= "string" or type(pkt.create) ~= "boolean" then
print("[server] server_player: invalid packet")
return
end
local player, err = select_player(srv, clt, pkt)
if err then
print("[server] failed to select player " .. tostring(clt.peer))
util.send(clt.peer, {
type = "client_player_fail",
error = err,
})
else
print("[server] select player " .. tostring(clt.peer) .. ": " .. player.name)
srv.players[player.name] = clt
clt.player = player
util.send(clt.peer, {
type = "client_player",
name = player.name,
})
broadcast_info(srv)
end
end
end
function server.update(srv)
local event = srv.host:service(20)
while event do
if event.type == "receive" then
local pkt = util.json_dec(event.data)
if pkt then
if event.peer == srv.match then
handle_match(srv, pkt)
else
handle_client(srv, event.peer, pkt)
end
end
elseif event.type == "connect" then
if event.peer == srv.match then
util.send(srv.match, { type = "match_register" })
end
print("[server] connect " .. tostring(event.peer))
elseif event.type == "disconnect" then
print("[server] disconnect " .. tostring(event.peer))
if event.peer == srv.match then
-- TODO
else
local clt = srv.clients[event.peer]
if clt then
disconnect(srv, clt)
end
end
end
event = srv.host:service()
end
end
function server.match_status(srv)
if srv.game_id then
return "active", srv.invite
elseif srv.match_req + 3 >= socket.gettime() then
return "wait"
else
return "fail"
end
end
function server.close(srv)
save_data(srv)
local peers = srv.host:peer_count()
for i = 1, peers do
srv.host:get_peer(i):disconnect()
end
srv.host:flush()
srv.host:destroy()
end
return server
|