From 05827d25733698a26cc0f305966e6a8a03be4684 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 25 Sep 2025 18:26:53 -0400 Subject: Game talks through CommonClient now --- apworld/client/client.gd | 381 +++++++++++------------------------------------ 1 file changed, 88 insertions(+), 293 deletions(-) (limited to 'apworld/client/client.gd') diff --git a/apworld/client/client.gd b/apworld/client/client.gd index 843647d..67edf29 100644 --- a/apworld/client/client.gd +++ b/apworld/client/client.gd @@ -2,20 +2,10 @@ extends Node const ap_version = {"major": 0, "minor": 6, "build": 3, "class": "Version"} -var SCRIPT_uuid +var SCRIPT_websocketserver -var _ws = WebSocketPeer.new() +var _server var _should_process = false -var _initiated_disconnect = false -var _try_wss = false -var _has_connected = false - -var _datapackages = {} -var _pending_packages = [] -var _item_id_to_name = {} # All games -var _location_id_to_name = {} # All games -var _item_name_to_id = {} # Lingo 2 only -var _location_name_to_id = {} # Lingo 2 only var _remote_version = {"major": 0, "minor": 0, "build": 0} var _gen_version = {"major": 0, "minor": 0, "build": 0} @@ -24,13 +14,9 @@ var ap_server = "" var ap_user = "" var ap_pass = "" -var _authenticated = false var _seed = "" var _team = 0 var _slot = 0 -var _players = [] -var _player_name_by_slot = {} -var _game_by_player = {} var _checked_locations = [] var _received_indexes = [] var _received_items = {} @@ -39,317 +25,135 @@ var _slot_data = {} signal could_not_connect signal connect_status signal client_connected(slot_data) -signal item_received(item_id, index, player, flags, amount) -signal message_received(message) -signal location_scout_received(item_id, location_id, player, flags) +signal item_received(item, amount) +signal location_scout_received(location_id, item_name, player_name, flags, for_self) +signal text_message_received(message) +signal item_sent_notification(message) +signal hint_received(message) func _init(): set_process_mode(Node.PROCESS_MODE_ALWAYS) - _ws.inbound_buffer_size = 8388608 - global._print("Instantiated APClient") - # Read AP datapackages from file, if there are any - if FileAccess.file_exists("user://ap_datapackages"): - var file = FileAccess.open("user://ap_datapackages", FileAccess.READ) - var data = file.get_var(true) - file.close() - - if typeof(data) != TYPE_DICTIONARY: - global._print("AP datapackages file is corrupted") - data = {} - - _datapackages = data - - processDatapackages() - func _ready(): - pass - #_ws.connect("connection_closed", _closed) - #_ws.connect("connection_failed", _closed) - #_ws.connect("server_disconnected", _closed) - #_ws.connect("connection_error", _errored) - #_ws.connect("connection_established", _connected) + _server = SCRIPT_websocketserver.new() + _server.client_connected.connect(_on_web_socket_server_client_connected) + _server.client_disconnected.connect(_on_web_socket_server_client_disconnected) + _server.message_received.connect(_on_web_socket_server_message_received) + add_child(_server) + _server.listen(43182) func _reset_state(): _should_process = false - _authenticated = false - _try_wss = false - _has_connected = false _received_items = {} _received_indexes = [] -func _errored(): - if _try_wss: - global._print("Could not connect to AP with ws://, now trying wss://") - connectToServer(ap_server, ap_user, ap_pass) - else: - global._print("AP connection failed") - _reset_state() +func disconnect_from_ap(): + sendMessage([{"cmd": "Disconnect"}]) - emit_signal( - "could_not_connect", - "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information." - ) +func _on_web_socket_server_client_connected(peer_id: int) -> void: + var peer: WebSocketPeer = _server.peers[peer_id] + print("Remote client connected: %d. Protocol: %s" % [peer_id, peer.get_selected_protocol()]) + _server.send(-peer_id, "[%d] connected" % peer_id) -func _closed(_was_clean = true): - global._print("Connection closed") - _reset_state() - if not _initiated_disconnect: - emit_signal("could_not_connect", "Disconnected from Archipelago") +func _on_web_socket_server_client_disconnected(peer_id: int) -> void: + var peer: WebSocketPeer = _server.peers[peer_id] + print( + ( + "Remote client disconnected: %d. Code: %d, Reason: %s" + % [peer_id, peer.get_close_code(), peer.get_close_reason()] + ) + ) + _server.send(-peer_id, "[%d] disconnected" % peer_id) - _initiated_disconnect = false +func _on_web_socket_server_message_received(_peer_id: int, packet: String) -> void: + global._print("Got data from server: " + packet) + var json = JSON.new() + var jserror = json.parse(packet) + if jserror != OK: + global._print("Error parsing packet from AP: " + jserror.error_string) + return -func _connected(_proto = ""): - global._print("Connected!") - _try_wss = false + for message in json.data: + var cmd = message["cmd"] + global._print("Received command: " + cmd) + if cmd == "Connected": + _seed = message["seed_name"] + _remote_version = message["version"] + _gen_version = message["generator_version"] + _team = message["team"] + _slot = message["slot"] + _checked_locations = message["checked_locations"] + _slot_data = message["slot_data"] -func disconnect_from_ap(): - _initiated_disconnect = true - _ws.close() - - -func _process(_delta): - if _should_process: - _ws.poll() - - var state = _ws.get_ready_state() - if state == WebSocketPeer.STATE_OPEN: - if not _has_connected: - _has_connected = true - - _connected() - - while _ws.get_available_packet_count(): - var packet = _ws.get_packet() - global._print("Got data from server: " + packet.get_string_from_utf8()) - var json = JSON.new() - var jserror = json.parse(packet.get_string_from_utf8()) - if jserror != OK: - global._print("Error parsing packet from AP: " + jserror.error_string) - return - - for message in json.data: - var cmd = message["cmd"] - global._print("Received command: " + cmd) - - if cmd == "RoomInfo": - _seed = message["seed_name"] - _remote_version = message["version"] - _gen_version = message["generator_version"] - - var needed_games = [] - for game in message["datapackage_checksums"].keys(): - if ( - !_datapackages.has(game) - or ( - _datapackages[game]["checksum"] - != message["datapackage_checksums"][game] - ) - ): - needed_games.append(game) - - if !needed_games.is_empty(): - _pending_packages = needed_games - var cur_needed = _pending_packages.pop_front() - requestDatapackages([cur_needed]) - else: - connectToRoom() - - elif cmd == "DataPackage": - for game in message["data"]["games"].keys(): - _datapackages[game] = message["data"]["games"][game] - saveDatapackages() - - if !_pending_packages.is_empty(): - var cur_needed = _pending_packages.pop_front() - requestDatapackages([cur_needed]) - else: - processDatapackages() - connectToRoom() - - elif cmd == "Connected": - _authenticated = true - _team = message["team"] - _slot = message["slot"] - _players = message["players"] - _checked_locations = message["checked_locations"] - _slot_data = message["slot_data"] - - for player in _players: - _player_name_by_slot[player["slot"]] = player["alias"] - _game_by_player[player["slot"]] = message["slot_info"][str( - player["slot"] - )]["game"] - - emit_signal("client_connected", _slot_data) - - elif cmd == "ConnectionRefused": - var error_message = "" - for error in message["errors"]: - var submsg = "" - if error == "InvalidSlot": - submsg = "Invalid player name." - elif error == "InvalidGame": - submsg = "The specified player is not playing Lingo." - elif error == "IncompatibleVersion": - submsg = ( - "The Archipelago server is not the correct version for this client. Expected v%d.%d.%d. Found v%d.%d.%d." - % [ - ap_version["major"], - ap_version["minor"], - ap_version["build"], - _remote_version["major"], - _remote_version["minor"], - _remote_version["build"] - ] - ) - elif error == "InvalidPassword": - submsg = "Incorrect password." - elif error == "InvalidItemsHandling": - submsg = "Invalid item handling flag. This is a bug with the client." - - if submsg != "": - if error_message != "": - error_message += " " - error_message += submsg - - if error_message == "": - error_message = "Unknown error." - - _initiated_disconnect = true - _ws.close() - - emit_signal("could_not_connect", error_message) - global._print("Connection to AP refused") - global._print(message) - - elif cmd == "ReceivedItems": - var i = 0 - for item in message["items"]: - var index = int(message["index"] + i) - i += 1 - - if _received_indexes.has(index): - # Do not re-process items. - continue - - _received_indexes.append(index) - - var item_id = int(item["item"]) - _received_items[item_id] = _received_items.get(item_id, 0) + 1 - - emit_signal( - "item_received", - item_id, - index, - int(item["player"]), - int(item["flags"]), - _received_items[item_id] - ) - - elif cmd == "PrintJSON": - emit_signal("message_received", message) - - elif cmd == "LocationInfo": - for loc in message["locations"]: - emit_signal( - "location_scout_received", - int(loc["item"]), - int(loc["location"]), - int(loc["player"]), - int(loc["flags"]) - ) - - elif state == WebSocketPeer.STATE_CLOSED: - if _has_connected: - _closed() - else: - _errored() - - -func saveDatapackages(): - # Save the AP datapackages to disk. - var file = FileAccess.open("user://ap_datapackages", FileAccess.WRITE) - file.store_var(_datapackages, true) - file.close() + client_connected.emit(_slot_data) + elif cmd == "ConnectionRefused": + could_not_connect.emit(message["text"]) + global._print("Connection to AP refused") -func connectToServer(server, un, pw): - ap_server = server - ap_user = un - ap_pass = pw + elif cmd == "ItemReceived": + for item in message["items"]: + var index = int(item["index"]) + if _received_indexes.has(index): + # Do not re-process items. + continue - _initiated_disconnect = false - - var url = "" - if ap_server.begins_with("ws://") or ap_server.begins_with("wss://"): - url = ap_server - _try_wss = false - elif _try_wss: - url = "wss://" + ap_server - _try_wss = false - else: - url = "ws://" + ap_server - _try_wss = true - - var err = _ws.connect_to_url(url) - if err != OK: - emit_signal( - "could_not_connect", - ( - "Could not connect to Archipelago. Check that your server and port are correct. See the error log for more information. Error code: %d." - % err - ) - ) - global._print("Could not connect to AP: %d" % err) - return - _should_process = true + _received_indexes.append(index) - emit_signal("connect_status", "Connecting...") + var item_id = int(item["id"]) + _received_items[item_id] = _received_items.get(item_id, 0) + 1 + item_received.emit(item, _received_items[item_id]) -func sendMessage(msg): - var payload = JSON.stringify(msg) - _ws.send_text(payload) + elif cmd == "TextMessage": + text_message_received.emit(message["data"]) + + elif cmd == "ItemSentNotif": + item_sent_notification.emit(message) + elif cmd == "HintReceived": + hint_received.emit(message) -func requestDatapackages(games): - emit_signal("connect_status", "Downloading %s data package..." % games[0]) + elif cmd == "LocationInfo": + for loc in message["locations"]: + location_scout_received.emit( + int(loc["id"]), + loc["item"], + loc["player"], + int(loc["flags"]), + int(loc["for_self"]) + ) - sendMessage([{"cmd": "GetDataPackage", "games": games}]) +func connectToServer(server, un, pw): + sendMessage([{"cmd": "Connect", "server": server, "player": un, "password": pw}]) + + ap_server = server + ap_user = un + ap_pass = pw -func processDatapackages(): - _item_id_to_name = {} - _location_id_to_name = {} - for game in _datapackages.keys(): - var package = _datapackages[game] + _should_process = true - _item_id_to_name[game] = {} - for item_name in package["item_name_to_id"].keys(): - _item_id_to_name[game][int(package["item_name_to_id"][item_name])] = item_name + connect_status.emit("Connecting...") - _location_id_to_name[game] = {} - for location_name in package["location_name_to_id"].keys(): - _location_id_to_name[game][int(package["location_name_to_id"][location_name])] = location_name - if _datapackages.has("Lingo 2"): - _item_name_to_id = _datapackages["Lingo 2"]["item_name_to_id"] - _location_name_to_id = _datapackages["Lingo 2"]["location_name_to_id"] +func sendMessage(msg): + var payload = JSON.stringify(msg) + _server.send(0, payload) func connectToRoom(): - emit_signal("connect_status", "Authenticating...") + connect_status.emit("Authenticating...") sendMessage( [ @@ -358,20 +162,11 @@ func connectToRoom(): "password": ap_pass, "game": "Lingo 2", "name": ap_user, - "uuid": SCRIPT_uuid.v4(), - "version": ap_version, - "items_handling": 0b111, # always receive our items - "tags": [], - "slot_data": true } ] ) -func sendConnectUpdate(tags): - sendMessage([{"cmd": "ConnectUpdate", "tags": tags}]) - - func requestSync(): sendMessage([{"cmd": "Sync"}]) -- cgit 1.4.1