From 60a0a573f0d0329e8d5c73878165ba80fa2d2628 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 8 Feb 2024 16:29:48 -0500 Subject: Initial commit --- racing/lobby.gd | 407 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 racing/lobby.gd (limited to 'racing/lobby.gd') diff --git a/racing/lobby.gd b/racing/lobby.gd new file mode 100644 index 0000000..87647bb --- /dev/null +++ b/racing/lobby.gd @@ -0,0 +1,407 @@ +extends Node + +var player_steam_id +var active_lobby_id = 0 +var active_lobby_members = [] +var next_message_id = 0 +var messages_needing_ack = {} +var is_vip = false +var is_ready = false +var everyone_ready = false +var members_to_join = [] +var is_starting = false + +const MAX_PLAYERS = 250 +const PROTOCOL_VERSION = 2 +const RECIPIENT_BROADCAST_ALL = -1 +const LOBBY_MAP_NAME = "ll1_racing" + +const LL1_AREAS = [ + ["Starting Room", 0, 0, 0], + ["Second Room", 0, 0, -15], + ["The Traveled", 34, 0, -18], + ["The Agreeable", 30, 0, -45], + ["The Colorful", 10, 0, -83], + ["Suits Area", 0, 0, -78, true], + ["Arrow Garden", -93, 1, -93], + ["The Wondrous (Table)", -108, 1, -78], + ["Courtyard", -64, 0, -71], + ["Yellow Backside Nine", -38, 0, -58], + ["Hot Crusts Area", -20, 0, -81], + ["Crossroads Corner", -28, 0, -54], + ["The Discerning", -54, 0, -34, true], + ["Green Backside", 22, 0, -94], + ["Observant Upstairs", 40, 9, -92, true], + ["Eight Room", 95, 15, -28], + ["The Perceptive", 60, 9, -57], + ["The Tenacious", 0, 0, -43], + ["Rainbow", -96, 0, -41], + ["The Undeterred", -87, 0, 25, true], + ["Directional Gallery", -57, 0, 0], + ["The Eyes They See", -54, 0, -23], + ["Tower First Floor", -27, 0, -23], + ["The Optimistic", 76, 0, -17], + ["The Initiated", 63, 0, -0, true], + ["Art Gallery", 92, 0, 15], + ["Art Gallery Top", 80, 30, 15], + ["Lookout", 75, 18, 51], + ["Knight Night Room", 37, 0, 7], + ["The Seeker", 9, 0, 16, true], + ["Hidden Room", 13, 0, 4], + ["Owl Hallway", 44, 0, -26], + ["Challenge Room", -9, 6, 13], + ["Pilgrim Room", -22, 0, 24, true], + ["Cellar Replica", -44, 0, 30], + ["Elements Area", -61, 0, 40], + ["The Artistic", -25, 0, 54, true], + ["Outside The Wise", -44, 0, 71], + ["The Wise", -72, 0, 72, true], + ["The Scientific", -18, 0, 89], + ["The Wanderer", 0, 0, 80], + ["The Fearless", 18, 10, 90], + ["Champion's Rest", 23, 0, 62, true], + ["The Steady", 31, 0, 77, true], + ["The Bold", 67, 0, 77, true], + ["Color Hunt", 45, 0, 69], + ["Room Room", 95, 6, 84], + ["The Bearer", 61, 0, 51], + ["Tower Third Floor", 18, 0, 33], + ["Rhyme Room (Cross)", 0, 9, 42], + ["Tower Seventh Floor", 0, 37, 64], +] + + +func _ready(): + installScriptExtension(ResourceLoader.load("user://maps/racing/load.gd")) + + # P2P solve messages should still be received while paused. + set_pause_mode(Node.PAUSE_MODE_PROCESS) + + # Undo the load screen removing our cursor + get_tree().get_root().set_disable_input(false) + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + + if global.get_node_or_null("RaceManager") == null: + var race_manager_script = load("user://maps/racing/manager.gd") + var race_manager = race_manager_script.new() + race_manager.name = "RaceManager" + + race_manager.SCRIPT_multiplayer = load("user://maps/racing/multiplayer.gd") + + global.add_child(race_manager) + + var _ignore = get_node("Panel/main_button").connect("pressed", self, "_main_button_pressed") + _ignore = get_node("Panel/return_button").connect("pressed", self, "_return_button_pressed") + + var dynamic_font = DynamicFont.new() + dynamic_font.font_data = load("res://fonts/Lingo.ttf") + dynamic_font.size = 36 + dynamic_font.outline_color = Color(0, 0, 0, 1) + dynamic_font.outline_size = 2 + get_node("Panel/ItemList").add_font_override("font", dynamic_font) + + get_node("Panel/title").text = "LINGO RACING LOBBY (%s)" % global.save_file + + player_steam_id = Steam.getSteamID() + + _ignore = Steam.connect("lobby_match_list", self, "_on_lobby_match_list") + _ignore = Steam.connect("lobby_created", self, "_on_lobby_created") + _ignore = Steam.connect("lobby_joined", self, "_on_lobby_joined") + _ignore = Steam.connect("lobby_data_update", self, "_on_lobby_data_update") + _ignore = Steam.connect("persona_state_change", self, "_on_persona_state_change") + _ignore = Steam.connect("p2p_session_request", self, "_on_p2p_session_request") + + _setup_recurring_task(5.0, "_resend_messages_needing_ack") + _setup_recurring_task(10.0, "_request_lobby_list") + + _request_lobby_list() + + +func _process(_delta: float) -> void: + _read_p2p_packet() + + +func _exit_tree(): + if active_lobby_id != 0 and !is_starting: + Steam.leaveLobby(active_lobby_id) + active_lobby_id = 0 + + +func _setup_recurring_task(wait_time, function): + var timer = Timer.new() + timer.set_wait_time(wait_time) + timer.set_one_shot(false) + timer.connect("timeout", self, function) + add_child(timer) + timer.start() + + +func _resend_messages_needing_ack(): + for message_id in messages_needing_ack: + var message = messages_needing_ack[message_id] + if message["recipient_id"] in active_lobby_members: + _send_p2p_packet(message["data"], message["recipient_id"], message["mode"], true) + else: + messages_needing_ack.erase(message_id) + + +func _request_lobby_list(): + Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE) + Steam.addRequestLobbyListNumericalFilter( + "protocol_version", PROTOCOL_VERSION, Steam.LOBBY_COMPARISON_EQUAL + ) + Steam.addRequestLobbyListStringFilter("map", LOBBY_MAP_NAME, Steam.LOBBY_COMPARISON_EQUAL) + Steam.addRequestLobbyListStringFilter( + "save_file", global.save_file.to_lower(), Steam.LOBBY_COMPARISON_EQUAL + ) + Steam.requestLobbyList() + + +func _on_lobby_match_list(lobbies: Array) -> void: + if active_lobby_id != 0 && not active_lobby_id in lobbies: + # Not sure why this happens, but it seems to sometimes. + lobbies.append(active_lobby_id) + var best_lobby_id = 0 + var best_lobby_size = -1 + for lobby_id in lobbies: + var lobby_size = Steam.getNumLobbyMembers(lobby_id) + if ( + lobby_size > best_lobby_size + || (lobby_size == best_lobby_size && lobby_id < best_lobby_id) + ): + best_lobby_id = lobby_id + best_lobby_size = lobby_size + if best_lobby_id == 0: + Steam.createLobby(Steam.LOBBY_TYPE_INVISIBLE, MAX_PLAYERS) + elif best_lobby_id != active_lobby_id: + Steam.joinLobby(best_lobby_id) + elif best_lobby_size <= 1: + _request_lobby_list() + + +func _on_lobby_created(result: int, lobby_id: int) -> void: + if result != Steam.RESULT_OK: + return + var _ignore = Steam.setLobbyData(lobby_id, "map", LOBBY_MAP_NAME) + _ignore = Steam.setLobbyData(lobby_id, "protocol_version", str(PROTOCOL_VERSION)) + _ignore = Steam.setLobbyData(lobby_id, "save_file", global.save_file.to_lower()) + _ignore = Steam.setLobbyData(lobby_id, "racing_vip", str(player_steam_id)) + is_vip = true + _request_lobby_list() + + +func _on_lobby_joined(lobby_id: int, _permissions: int, _locked: bool, result: int) -> void: + if result != Steam.RESULT_OK: + return + if active_lobby_id != 0 && active_lobby_id != lobby_id: + Steam.leaveLobby(active_lobby_id) + active_lobby_id = lobby_id + if Steam.getLobbyData(lobby_id, "racing_vip") == str(player_steam_id): + is_vip = true + _update_lobby_members() + + +func _on_lobby_data_update(_lobby_id: int, _member_id: int, _key: int) -> void: + _update_lobby_members() + + +func _on_persona_state_change(_persona_id: int, _flag: int) -> void: + _update_lobby_members() + + +func _update_lobby_members(): + if active_lobby_id == 0: + return + var lobby_size: int = Steam.getNumLobbyMembers(active_lobby_id) + var itemlist = get_node("Panel/ItemList") + itemlist.clear() + active_lobby_members.clear() + var temp_everyone_ready = true + for i in range(0, lobby_size): + var member_id: int = Steam.getLobbyMemberByIndex(active_lobby_id, i) + var steam_name: String = Steam.getFriendPersonaName(member_id) + itemlist.add_item(steam_name, null, false) + active_lobby_members.append(member_id) + + var mem_is_ready = Steam.getLobbyMemberData(active_lobby_id, member_id, "ready") == "true" + if mem_is_ready: + itemlist.set_item_custom_fg_color(itemlist.get_item_count() - 1, Color.green) + else: + temp_everyone_ready = false + everyone_ready = temp_everyone_ready + var main_button = get_node("Panel/main_button") + if everyone_ready and is_vip: + main_button.text = "START GAME" + main_button.disabled = false + elif is_ready and is_vip: + main_button.text = "START GAME" + main_button.disabled = true + elif is_ready: + main_button.text = "READY" + main_button.disabled = true + else: + main_button.text = "READY" + main_button.disabled = false + + +func _on_p2p_session_request(remote_id: int) -> void: + if remote_id in active_lobby_members: + var _ignore = Steam.acceptP2PSessionWithUser(remote_id) + + +func _read_p2p_packet() -> void: + var packet_size: int = Steam.getAvailableP2PPacketSize(0) + if packet_size > 0: + var packet: Dictionary = Steam.readP2PPacket(packet_size, 0) + var remote_id = packet["steam_id_remote"] + if remote_id in active_lobby_members: + var serialized: PoolByteArray = packet["data"] + var data: Dictionary = bytes2var(serialized, false) + if "message_id" in data: + _send_p2p_packet( + { + "ack": data["message_id"], + }, + remote_id, + Steam.P2P_SEND_RELIABLE_WITH_BUFFERING, + false + ) + if "start_x" in data: + var race_manager = global.get_node("RaceManager") + race_manager.start_pos = [ + data["start_name"], + int(data["start_x"]), + int(data["start_y"]), + int(data["start_z"]) + ] + race_manager.end_pos = [ + data["end_name"], int(data["end_x"]), int(data["end_y"]), int(data["end_z"]) + ] + + is_starting = true + _start_game() + if "ack" in data: + messages_needing_ack.erase(data["ack"]) + + if is_starting: + members_to_join.erase(remote_id) + + if members_to_join.empty(): + _start_game() + + +func _send_p2p_packet(data: Dictionary, recipient_id: int, mode: int, needs_ack: bool) -> void: + if recipient_id == RECIPIENT_BROADCAST_ALL: + for member_id in active_lobby_members: + _send_p2p_packet(data.duplicate(), member_id, mode, needs_ack) + return + + if needs_ack: + var message_id + if "message_id" in data: + message_id = data["message_id"] + else: + message_id = next_message_id + next_message_id += 1 + data["message_id"] = message_id + if not message_id in messages_needing_ack: + messages_needing_ack[message_id] = { + "data": data, + "recipient_id": recipient_id, + "mode": mode, + } + var serialized: PoolByteArray = [] + serialized.append_array(var2bytes(data)) + var _ignore = Steam.sendP2PPacket(recipient_id, serialized, mode) + + +func _main_button_pressed(): + if everyone_ready and is_vip: + get_node("Panel/main_button").disabled = true + + var rng = RandomNumberGenerator.new() + rng.randomize() + + var start_pos + var end_pos + var found = false + while !found: + var areas_dupe = LL1_AREAS.duplicate() + var i = rng.randi_range(0, areas_dupe.size() - 1) + start_pos = areas_dupe[i] + areas_dupe.remove(i) + i = rng.randi_range(0, areas_dupe.size() - 1) + end_pos = areas_dupe[i] + + var start_vec = Vector3(start_pos[1], start_pos[2], start_pos[3]) + var end_vec = Vector3(end_pos[1], end_pos[2], end_pos[3]) + if start_vec.distance_to(end_vec) > 50 and not (start_pos.size() >= 5 and start_pos[4]): + found = true + + members_to_join = active_lobby_members.duplicate() + members_to_join.erase(player_steam_id) + is_starting = true + + var race_manager = global.get_node("RaceManager") + race_manager.start_pos = start_pos + race_manager.end_pos = end_pos + + if active_lobby_members.size() == 1: + _start_game() + else: + _send_p2p_packet( + { + "start_name": start_pos[0], + "start_x": str(start_pos[1]), + "start_y": str(start_pos[2]), + "start_z": str(start_pos[3]), + "end_name": end_pos[0], + "end_x": str(end_pos[1]), + "end_y": str(end_pos[2]), + "end_z": str(end_pos[3]), + }, + RECIPIENT_BROADCAST_ALL, + Steam.P2P_SEND_RELIABLE, + true + ) + else: + Steam.setLobbyMemberData(active_lobby_id, "ready", "true") + is_ready = true + _update_lobby_members() + + +func _return_button_pressed(): + _exit_tree() + fader._fade_start("main_menu") + + +func _start_game(): + var race_manager = global.get_node("RaceManager") + race_manager.lobby_id = active_lobby_id + + # Switch to LL1 + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + global.map = "level1" + global.entry_point = Vector3( + race_manager.start_pos[1], race_manager.start_pos[2] + 0.25, race_manager.start_pos[3] + ) + global.entry_rotate = Vector3(0, 0, 0) + global.sets_entry_point = true + var _discard = get_tree().change_scene("res://scenes/load_screen.tscn") + + +# 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) -- cgit 1.4.1