From 3f53502a5907ed1982d28a392c54331f0c1c2c42 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 25 Sep 2025 12:09:50 -0400 Subject: Move the client into the apworld Only works on source right now, not as an apworld. --- apworld/client/main.gd | 284 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 apworld/client/main.gd (limited to 'apworld/client/main.gd') diff --git a/apworld/client/main.gd b/apworld/client/main.gd new file mode 100644 index 0000000..a31eb89 --- /dev/null +++ b/apworld/client/main.gd @@ -0,0 +1,284 @@ +extends Node + + +func _ready(): + var runtime = global.get_node("Runtime") + + # Some helpful logging. + if Steam.isSubscribed(): + global._print("Provisioning successful! Build ID: %d" % Steam.getAppBuildId()) + else: + global._print("Provisioning failed.") + + # Undo the load screen removing our cursor + get_tree().get_root().set_disable_input(false) + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + + # Increase the WebSocket input buffer size so that we can download large + # data packages. + ProjectSettings.set_setting("network/limits/websocket_client/max_in_buffer_kb", 8192) + + switcher.layer = 4 + + # Create the global AP manager, if it doesn't already exist. + if not global.has_node("Archipelago"): + var ap_script = runtime.load_path("manager.gd") + var ap_instance = ap_script.new() + ap_instance.name = "Archipelago" + + ap_instance.SCRIPT_client = runtime.load_path("client.gd") + ap_instance.SCRIPT_keyboard = runtime.load_path("keyboard.gd") + ap_instance.SCRIPT_locationListener = runtime.load_path("locationListener.gd") + ap_instance.SCRIPT_minimap = runtime.load_path("minimap.gd") + ap_instance.SCRIPT_uuid = runtime.load_path("vendor/uuid.gd") + ap_instance.SCRIPT_victoryListener = runtime.load_path("victoryListener.gd") + + global.add_child(ap_instance) + + # Let's also inject any scripts we need to inject now. + installScriptExtension(runtime.load_path("animationListener.gd")) + installScriptExtension(runtime.load_path("collectable.gd")) + installScriptExtension(runtime.load_path("door.gd")) + installScriptExtension(runtime.load_path("keyHolder.gd")) + installScriptExtension(runtime.load_path("keyHolderChecker.gd")) + installScriptExtension(runtime.load_path("keyHolderResetterListener.gd")) + installScriptExtension(runtime.load_path("painting.gd")) + installScriptExtension(runtime.load_path("panel.gd")) + installScriptExtension(runtime.load_path("pauseMenu.gd")) + installScriptExtension(runtime.load_path("player.gd")) + installScriptExtension(runtime.load_path("saver.gd")) + installScriptExtension(runtime.load_path("teleport.gd")) + installScriptExtension(runtime.load_path("teleportListener.gd")) + installScriptExtension(runtime.load_path("visibilityListener.gd")) + installScriptExtension(runtime.load_path("worldport.gd")) + installScriptExtension(runtime.load_path("worldportListener.gd")) + + var proto_script = runtime.load_path("../generated/proto.gd") + var gamedata_script = runtime.load_path("gamedata.gd") + var gamedata_instance = gamedata_script.new(proto_script) + gamedata_instance.load(runtime.read_path("../generated/data.binpb")) + gamedata_instance.name = "Gamedata" + global.add_child(gamedata_instance) + + var messages_script = runtime.load_path("messages.gd") + var messages_instance = messages_script.new() + messages_instance.name = "Messages" + messages_instance.SCRIPT_rainbowText = runtime.load_path("rainbowText.gd") + global.add_child(messages_instance) + + var textclient_script = runtime.load_path("textclient.gd") + var textclient_instance = textclient_script.new() + textclient_instance.name = "Textclient" + global.add_child(textclient_instance) + + var compass_overlay_script = runtime.load_path("compass_overlay.gd") + var compass_overlay_instance = compass_overlay_script.new() + compass_overlay_instance.name = "Compass" + compass_overlay_instance.SCRIPT_compass = runtime.load_path("compass.gd") + global.add_child(compass_overlay_instance) + + var ap = global.get_node("Archipelago") + var gamedata = global.get_node("Gamedata") + ap.connect("ap_connected", connectionSuccessful) + ap.connect("could_not_connect", connectionUnsuccessful) + ap.connect("connect_status", connectionStatus) + + # Populate textboxes with AP settings. + get_node("../Panel/server_box").text = ap.ap_server + get_node("../Panel/player_box").text = ap.ap_user + get_node("../Panel/password_box").text = ap.ap_pass + + var history_box = get_node("../Panel/connection_history") + if ap.connection_history.is_empty(): + history_box.disabled = true + else: + history_box.disabled = false + + var i = 0 + for details in ap.connection_history: + history_box.get_popup().add_item("%s (%s)" % [details[1], details[0]], i) + i += 1 + + history_box.get_popup().connect("id_pressed", historySelected) + + # Show client version. + get_node("../Panel/title").text = ( + "ARCHIPELAGO (%d.%d)" % [gamedata.objects.get_version(), ap.MOD_VERSION] + ) + + # Increase font size in text boxes. + get_node("../Panel/server_box").add_theme_font_size_override("font_size", 36) + get_node("../Panel/player_box").add_theme_font_size_override("font_size", 36) + get_node("../Panel/password_box").add_theme_font_size_override("font_size", 36) + + # Set up version mismatch dialog. + get_node("../Panel/VersionMismatch").connect("confirmed", startGame) + get_node("../Panel/VersionMismatch").get_cancel_button().pressed.connect( + versionMismatchDeclined + ) + + # Set up buttons. + get_node("../Panel/connect_button").connect("pressed", _connect_pressed) + get_node("../Panel/quit_button").connect("pressed", _back_pressed) + + +func _connect_pressed(): + get_node("../Panel/connect_button").disabled = true + + var ap = global.get_node("Archipelago") + ap.ap_server = get_node("../Panel/server_box").text + ap.ap_user = get_node("../Panel/player_box").text + ap.ap_pass = get_node("../Panel/password_box").text + ap.saveSettings() + + ap.connectToServer() + + +func _back_pressed(): + var ap = global.get_node("Archipelago") + ap.disconnect_from_ap() + + get_tree().quit() + + +# Adapted from https://gitlab.com/Delta-V-Modding/Mods/-/blob/main/game/ModLoader.gd +func installScriptExtension(childScript: Resource): + # Force Godot to compile the script now. + # We need to do this here to ensure that the inheritance chain is + # properly set up, and multiple mods can chain-extend the same + # class multiple times. + # This is also needed to make Godot instantiate the extended class + # when creating singletons. + # The actual instance is thrown away. + childScript.new() + + var parentScript = childScript.get_base_script() + var parentScriptPath = parentScript.resource_path + global._print("ModLoader: Installing script extension over %s" % parentScriptPath) + childScript.take_over_path(parentScriptPath) + + +func connectionStatus(message): + var popup = get_node("../Panel/AcceptDialog") + popup.title = "Connecting to Archipelago" + popup.dialog_text = message + popup.exclusive = true + popup.get_ok_button().visible = false + popup.popup_centered() + + +func connectionSuccessful(): + var ap = global.get_node("Archipelago") + var gamedata = global.get_node("Gamedata") + + # Check for major version mismatch. + if ap.apworld_version[0] != gamedata.objects.get_version(): + get_node("../Panel/AcceptDialog").exclusive = false + + var popup = get_node("../Panel/VersionMismatch") + popup.title = "Version Mismatch!" + popup.dialog_text = ( + "This slot was generated using v%d.%d of the Lingo 2 apworld,\nwhich has a different major version than this client (v%d.%d).\nIt is highly recommended to play using the correct version of the client.\nYou may experience bugs or logic issues if you continue." + % [ + ap.apworld_version[0], + ap.apworld_version[1], + gamedata.objects.get_version(), + ap.MOD_VERSION + ] + ) + popup.exclusive = true + popup.popup_centered() + + return + + startGame() + + +func startGame(): + var ap = global.get_node("Archipelago") + + # Save connection details + var connection_details = [ap.ap_server, ap.ap_user, ap.ap_pass] + if ap.connection_history.has(connection_details): + ap.connection_history.erase(connection_details) + ap.connection_history.push_front(connection_details) + if ap.connection_history.size() > 10: + ap.connection_history.resize(10) + ap.saveSettings() + + # Switch to the_entry + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + global.user = ap.getSaveFileName() + global.universe = "lingo" + global.map = "the_entry" + + unlocks.resetCollectables() + unlocks.resetData() + + ap.setup_keys() + + unlocks.loadCollectables() + unlocks.loadData() + unlocks.unlockKey("capslock", 1) + + if ap.shuffle_worldports: + settings.worldport_fades = "default" + else: + settings.worldport_fades = "never" + + clearResourceCache("res://objects/meshes/gridDoor.tscn") + clearResourceCache("res://objects/nodes/collectable.tscn") + clearResourceCache("res://objects/nodes/door.tscn") + clearResourceCache("res://objects/nodes/keyHolder.tscn") + clearResourceCache("res://objects/nodes/listeners/animationListener.tscn") + clearResourceCache("res://objects/nodes/listeners/keyHolderChecker.tscn") + clearResourceCache("res://objects/nodes/listeners/keyHolderResetterListener.tscn") + clearResourceCache("res://objects/nodes/listeners/teleportListener.tscn") + clearResourceCache("res://objects/nodes/listeners/visibilityListener.tscn") + clearResourceCache("res://objects/nodes/listeners/worldportListener.tscn") + clearResourceCache("res://objects/nodes/panel.tscn") + clearResourceCache("res://objects/nodes/player.tscn") + clearResourceCache("res://objects/nodes/saver.tscn") + clearResourceCache("res://objects/nodes/teleport.tscn") + clearResourceCache("res://objects/nodes/worldport.tscn") + clearResourceCache("res://objects/scenes/menus/pause_menu.tscn") + + var paintings_dir = DirAccess.open("res://objects/meshes/paintings") + if paintings_dir: + paintings_dir.list_dir_begin() + var file_name = paintings_dir.get_next() + while file_name != "": + if not paintings_dir.current_is_dir() and file_name.ends_with(".tscn"): + clearResourceCache("res://objects/meshes/paintings/" + file_name) + file_name = paintings_dir.get_next() + + switcher.switch_map.call_deferred("res://objects/scenes/the_entry.tscn") + + +func connectionUnsuccessful(error_message): + get_node("../Panel/connect_button").disabled = false + + var popup = get_node("../Panel/AcceptDialog") + popup.title = "Could not connect to Archipelago" + popup.dialog_text = error_message + popup.exclusive = true + popup.get_ok_button().visible = true + popup.popup_centered() + + +func versionMismatchDeclined(): + get_node("../Panel/AcceptDialog").hide() + get_node("../Panel/connect_button").disabled = false + + +func historySelected(index): + var ap = global.get_node("Archipelago") + var details = ap.connection_history[index] + + get_node("../Panel/server_box").text = details[0] + get_node("../Panel/player_box").text = details[1] + get_node("../Panel/password_box").text = details[2] + + +func clearResourceCache(path): + ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE) -- cgit 1.4.1