-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserver.gd
170 lines (151 loc) · 4.04 KB
/
server.gd
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
# the server part. currently only implements chat
# execute this using godot -s httpd.gd
extends SceneTree
var srv = TCP_Server.new()
var csts = []
var nicks = []
var cst_mt = Mutex.new()
func add_cst(cst):
cst_mt.lock()
csts.push_back(cst)
cst_mt.unlock()
func rem_cst(cst):
cst_mt.lock()
var idx = csts.find(cst)
if (idx >= 0):
csts.remove(idx)
cst_mt.unlock()
func add_nick(nick):
cst_mt.lock()
nicks.push_back(nick)
cst_mt.unlock()
func rem_nick(nick):
cst_mt.lock()
var idx = nicks.find(nick)
if (idx >= 0):
nicks.remove(idx)
cst_mt.unlock()
func print_nicks():
cst_mt.lock()
var r = ""
var first = true
for nick in nicks:
if (first):
r = nick
else:
r = str(r, ", ", nick)
print(str("nicks online: ", r))
cst_mt.unlock()
func nick_exists(nick):
cst_mt.lock()
var idx = nicks.find(nick)
var ret = (idx >= 0)
cst_mt.unlock()
return ret
func send_nicks(cst):
cst_mt.lock()
cst.put_var(["nicks", nicks])
cst_mt.unlock()
func send_to_all(content):
cst_mt.lock()
print(str("send: ", content))
for cst in csts:
# TODO add connected check here, we might get a msg from sb right after one closed connection, and before the cst got removed
cst.put_var(content)
cst_mt.unlock()
func nick_is_valid(nick):
var nl = nick.length()
for i in range(nl):
var c = nick[i]
if (not((("a" < c) and (c < "z")) or (("A" < c) and (c < "Z")) or (("0" < c) and (c < "9")) or (c == "_"))):
return false
return true
func ping_ok(ctr):
return ctr < 120000
func run_thrd(params):
var con = params.con
#if (con.is_connected()):
# print("connection is connected")
#else:
# print("connection is NOT connected")
var cst = PacketPeerStream.new()
cst.set_stream_peer(con)
add_cst(cst)
send_nicks(cst)
var first = true
var nick = "Guest_" + str(randi() % 999)
var last_ping_ctr = 0
var ping_sent = false
var timeout = false
var closecon
while (con.is_connected()):
while (cst.get_available_packet_count() == 0 and con.is_connected() and last_ping_ctr < 1200): # TODO replace this with actual blocking
last_ping_ctr = last_ping_ctr + 1
OS.delay_msec(100)
if (last_ping_ctr >= 1000 and !ping_sent):
ping_sent = true
cst_mt.lock()
cst.put_var(["ping", ""])
cst_mt.unlock()
if (last_ping_ctr >= 1200):
timeout = true
break
if (not con.is_connected()):
break
var v = cst.get_var()
if ((typeof(v) == TYPE_ARRAY) and (v.size() >= 2)):
var m = v[0]
if (m == "chat"):
send_to_all(["chat", nick, v[1]])
elif (m == "nick"):
var newnick = v[1]
if !(nick_exists(newnick) and nick_is_valid(newnick)):
if (not first):
send_to_all(["nick", nick, newnick])
rem_nick(nick)
nick = newnick
add_nick(nick)
else:
if (first):
# TODO think of message to send to user "sorry you sent no nick, or your nick was invalid, your nick is now: ABC"
elif (m == "pong"):
last_ping_ctr = 0
ping_sent = false
else:
print(str("client sent unsupported msg '", m, "'!"))
pass
if (first):
first = false
add_nick(nick)
print_nicks()
send_to_all(["join", nick])
rem_cst(cst)
rem_nick(nick)
print_nicks()
if (!timeout):
send_to_all(["part", nick])
else:
send_to_all(["timeout", nick])
cst_mt.lock()
con.disconnect()
cst_mt.unlock()
# hack to free the thread reference after it has exited
# godot has no native protection here, and can
# free a running thread if all references are lost
# The call below saves the reference until the method
# can be called, and gives additional safety by calling
# wait_to_finish and not some arbitrary method, to account for
# the engine or the OS doing other tasks on the thread
# before actually declaring a thread to be "finished"
params.thread.call_deferred("wait_to_finish")
func _init():
var port = 40000
srv.listen(port)
print(str("Server listening at port ", port))
while (true):
while (!srv.is_connection_available()): # TODO replace this with actual blocking
OS.delay_msec(100)
var cn = srv.take_connection()
var thread = Thread.new()
thread.start(self, "run_thrd", {con=cn, thread=thread})
quit()