diff options
Diffstat (limited to 'apworld/client/vendor/WebSocketServer.gd')
-rw-r--r-- | apworld/client/vendor/WebSocketServer.gd | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/apworld/client/vendor/WebSocketServer.gd b/apworld/client/vendor/WebSocketServer.gd new file mode 100644 index 0000000..2cee494 --- /dev/null +++ b/apworld/client/vendor/WebSocketServer.gd | |||
@@ -0,0 +1,173 @@ | |||
1 | class_name WebSocketServer | ||
2 | extends Node | ||
3 | |||
4 | signal message_received(peer_id: int, message: String) | ||
5 | signal client_connected(peer_id: int) | ||
6 | signal client_disconnected(peer_id: int) | ||
7 | |||
8 | @export var handshake_headers := PackedStringArray() | ||
9 | @export var supported_protocols := PackedStringArray() | ||
10 | @export var handshake_timout := 3000 | ||
11 | @export var use_tls := false | ||
12 | @export var tls_cert: X509Certificate | ||
13 | @export var tls_key: CryptoKey | ||
14 | @export var refuse_new_connections := false: | ||
15 | set(refuse): | ||
16 | if refuse: | ||
17 | pending_peers.clear() | ||
18 | |||
19 | |||
20 | class PendingPeer: | ||
21 | var connect_time: int | ||
22 | var tcp: StreamPeerTCP | ||
23 | var connection: StreamPeer | ||
24 | var ws: WebSocketPeer | ||
25 | |||
26 | func _init(p_tcp: StreamPeerTCP) -> void: | ||
27 | tcp = p_tcp | ||
28 | connection = p_tcp | ||
29 | connect_time = Time.get_ticks_msec() | ||
30 | |||
31 | |||
32 | var tcp_server := TCPServer.new() | ||
33 | var pending_peers: Array[PendingPeer] = [] | ||
34 | var peers: Dictionary | ||
35 | |||
36 | |||
37 | func listen(port: int) -> int: | ||
38 | assert(not tcp_server.is_listening()) | ||
39 | return tcp_server.listen(port) | ||
40 | |||
41 | |||
42 | func stop() -> void: | ||
43 | tcp_server.stop() | ||
44 | pending_peers.clear() | ||
45 | peers.clear() | ||
46 | |||
47 | |||
48 | func send(peer_id: int, message: String) -> int: | ||
49 | var type := typeof(message) | ||
50 | if peer_id <= 0: | ||
51 | # Send to multiple peers, (zero = broadcast, negative = exclude one). | ||
52 | for id: int in peers: | ||
53 | if id == -peer_id: | ||
54 | continue | ||
55 | if type == TYPE_STRING: | ||
56 | peers[id].send_text(message) | ||
57 | else: | ||
58 | peers[id].put_packet(message) | ||
59 | return OK | ||
60 | |||
61 | assert(peers.has(peer_id)) | ||
62 | var socket: WebSocketPeer = peers[peer_id] | ||
63 | if type == TYPE_STRING: | ||
64 | return socket.send_text(message) | ||
65 | return socket.send(var_to_bytes(message)) | ||
66 | |||
67 | |||
68 | func get_message(peer_id: int) -> Variant: | ||
69 | assert(peers.has(peer_id)) | ||
70 | var socket: WebSocketPeer = peers[peer_id] | ||
71 | if socket.get_available_packet_count() < 1: | ||
72 | return null | ||
73 | var pkt: PackedByteArray = socket.get_packet() | ||
74 | if socket.was_string_packet(): | ||
75 | return pkt.get_string_from_utf8() | ||
76 | return bytes_to_var(pkt) | ||
77 | |||
78 | |||
79 | func has_message(peer_id: int) -> bool: | ||
80 | assert(peers.has(peer_id)) | ||
81 | return peers[peer_id].get_available_packet_count() > 0 | ||
82 | |||
83 | |||
84 | func _create_peer() -> WebSocketPeer: | ||
85 | var ws := WebSocketPeer.new() | ||
86 | ws.supported_protocols = supported_protocols | ||
87 | ws.handshake_headers = handshake_headers | ||
88 | return ws | ||
89 | |||
90 | |||
91 | func poll() -> void: | ||
92 | if not tcp_server.is_listening(): | ||
93 | return | ||
94 | |||
95 | while not refuse_new_connections and tcp_server.is_connection_available(): | ||
96 | var conn: StreamPeerTCP = tcp_server.take_connection() | ||
97 | assert(conn != null) | ||
98 | pending_peers.append(PendingPeer.new(conn)) | ||
99 | |||
100 | var to_remove := [] | ||
101 | |||
102 | for p in pending_peers: | ||
103 | if not _connect_pending(p): | ||
104 | if p.connect_time + handshake_timout < Time.get_ticks_msec(): | ||
105 | # Timeout. | ||
106 | to_remove.append(p) | ||
107 | continue # Still pending. | ||
108 | |||
109 | to_remove.append(p) | ||
110 | |||
111 | for r: RefCounted in to_remove: | ||
112 | pending_peers.erase(r) | ||
113 | |||
114 | to_remove.clear() | ||
115 | |||
116 | for id: int in peers: | ||
117 | var p: WebSocketPeer = peers[id] | ||
118 | p.poll() | ||
119 | |||
120 | if p.get_ready_state() != WebSocketPeer.STATE_OPEN: | ||
121 | client_disconnected.emit(id) | ||
122 | to_remove.append(id) | ||
123 | continue | ||
124 | |||
125 | while p.get_available_packet_count(): | ||
126 | message_received.emit(id, get_message(id)) | ||
127 | |||
128 | for r: int in to_remove: | ||
129 | peers.erase(r) | ||
130 | to_remove.clear() | ||
131 | |||
132 | |||
133 | func _connect_pending(p: PendingPeer) -> bool: | ||
134 | if p.ws != null: | ||
135 | # Poll websocket client if doing handshake. | ||
136 | p.ws.poll() | ||
137 | var state := p.ws.get_ready_state() | ||
138 | if state == WebSocketPeer.STATE_OPEN: | ||
139 | var id := randi_range(2, 1 << 30) | ||
140 | peers[id] = p.ws | ||
141 | client_connected.emit(id) | ||
142 | return true # Success. | ||
143 | elif state != WebSocketPeer.STATE_CONNECTING: | ||
144 | return true # Failure. | ||
145 | return false # Still connecting. | ||
146 | elif p.tcp.get_status() != StreamPeerTCP.STATUS_CONNECTED: | ||
147 | return true # TCP disconnected. | ||
148 | elif not use_tls: | ||
149 | # TCP is ready, create WS peer. | ||
150 | p.ws = _create_peer() | ||
151 | p.ws.accept_stream(p.tcp) | ||
152 | return false # WebSocketPeer connection is pending. | ||
153 | |||
154 | else: | ||
155 | if p.connection == p.tcp: | ||
156 | assert(tls_key != null and tls_cert != null) | ||
157 | var tls := StreamPeerTLS.new() | ||
158 | tls.accept_stream(p.tcp, TLSOptions.server(tls_key, tls_cert)) | ||
159 | p.connection = tls | ||
160 | p.connection.poll() | ||
161 | var status: StreamPeerTLS.Status = p.connection.get_status() | ||
162 | if status == StreamPeerTLS.STATUS_CONNECTED: | ||
163 | p.ws = _create_peer() | ||
164 | p.ws.accept_stream(p.connection) | ||
165 | return false # WebSocketPeer connection is pending. | ||
166 | if status != StreamPeerTLS.STATUS_HANDSHAKING: | ||
167 | return true # Failure. | ||
168 | |||
169 | return false | ||
170 | |||
171 | |||
172 | func _process(_delta: float) -> void: | ||
173 | poll() | ||