diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-25 18:26:53 -0400 |
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-25 18:26:53 -0400 |
| commit | 05827d25733698a26cc0f305966e6a8a03be4684 (patch) | |
| tree | 75dbec594a0bd0c2494df31b712bb6435730197c /apworld/client/vendor/WebSocketServer.gd | |
| parent | cb2eca4fed1eb3692eaa13715f65ebcaf8472b64 (diff) | |
| download | lingo2-archipelago-05827d25733698a26cc0f305966e6a8a03be4684.tar.gz lingo2-archipelago-05827d25733698a26cc0f305966e6a8a03be4684.tar.bz2 lingo2-archipelago-05827d25733698a26cc0f305966e6a8a03be4684.zip | |
Game talks through CommonClient now
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() | ||
