diff --git a/assets/core/networking/NetworkManager.gd b/assets/core/networking/NetworkManager.gd index 6a39f7c..7697f21 100644 --- a/assets/core/networking/NetworkManager.gd +++ b/assets/core/networking/NetworkManager.gd @@ -1,10 +1,17 @@ class_name NetworkManager extends Node -var steamId: int +const PACKET_READ_LIMIT: int = 32 + +var steam_id: int var username: String var avatar_texture: ImageTexture +var is_host: bool = false +var lobby_id: int = 0 +var lobby_members: Array = [] +var lobby_members_max: int = 4 + func _ready() -> void: var init_result = Steam.steamInit(true, 480) if init_result["status"] <= 1: @@ -14,14 +21,24 @@ func _ready() -> void: func _initialize() -> void: - steamId = Steam.getSteamID() - username = Steam.getFriendPersonaName(steamId) + steam_id = Steam.getSteamID() + username = Steam.getFriendPersonaName(steam_id) GameConsole.print_line("Steam ID: " + username) Steam.getPlayerAvatar() Steam.avatar_loaded.connect(_on_avatar_loaded) + Steam.lobby_created.connect(_on_lobby_created) + Steam.lobby_joined.connect(_on_lobby_joined) + Steam.p2p_session_request.connect(_on_p2p_session_request) + +func _process(_delta: float) -> void: + Steam.run_callbacks() + if lobby_id > 0: + read_all_p2p_packets() + + func _on_avatar_loaded(_user_id: int, avatar_size: int, avatar_buffer: PackedByteArray) -> void: var avatar_image: Image = Image.create_from_data(avatar_size, avatar_size, false, Image.FORMAT_RGBA8, avatar_buffer) if avatar_size > 128: @@ -30,5 +47,98 @@ func _on_avatar_loaded(_user_id: int, avatar_size: int, avatar_buffer: PackedByt if avatar_image != null: avatar_texture = ImageTexture.create_from_image(avatar_image) + GameConsole.print_line("Avatar loaded!") else: GameConsole.log_error("Failed to create avatar image!") + + +func _on_lobby_created(connection_status: int, this_lobby_id: int) -> void: + if connection_status == 1: + lobby_id = this_lobby_id + Steam.setLobbyJoinable(lobby_id, true) + Steam.setLobbyData(lobby_id, "host", str(steam_id)) + var set_relay: bool = Steam.allowP2PPacketRelay(true) + + GameConsole.print_line("Lobby created: " + str(lobby_id)) + + +func _on_lobby_joined(this_lobby_id: int, _permissions: int, _locked: bool, response: int) -> void: + if response == Steam.CHAT_ROOM_ENTER_RESPONSE_SUCCESS: + lobby_id = this_lobby_id + get_lobby_members() + make_p2p_handshake() + + +func read_all_p2p_packets(read_count: int = 0) -> void: + if read_count >= PACKET_READ_LIMIT: + return + + if Steam.getAvailableP2PPacketSize(0) > 0: + read_p2p_packet() + read_all_p2p_packets(read_count + 1) + + +func _on_p2p_session_request(remote_id: int) -> void: + var this_requester: String = Steam.getFriendPersonaName(remote_id) + Steam.acceptP2PSessionWithUser(remote_id) + + +func make_p2p_handshake() -> void: + send_p2p_packet(0, {"message" : "handshake", "steam_id" : steam_id, "username" : username}) + + +func create_lobby() -> void: + if lobby_id == 0: + is_host = true + Steam.createLobby(Steam.LOBBY_TYPE_PUBLIC, lobby_members_max) + + +func join_lobby(this_lobby_id: int) -> void: + Steam.joinLobby(lobby_id) + + +func get_lobby_members(): + lobby_members.clear() + var member_count: int = Steam.getNumLobbyMembers(lobby_id) + + for member in range(0,member_count): + var member_id: int = Steam.getLobbyMemberByIndex(lobby_id, member) + var member_username: String = Steam.getFriendPersonaName(member_id) + lobby_members.append({"steam_id": member_id, "username": member_username}) + + + +func send_p2p_packet(this_target: int, packet_data: Dictionary, send_type: int = 0) -> void: + var channel: int = 0 + + var this_data: PackedByteArray + this_data.append_array(var_to_bytes(packet_data)) + + if this_target == 0: + if lobby_members.size() > 1: + for member in lobby_members: + if member["steam_id"] != steam_id: + Steam.sendP2PPacket(member["steam_id"], this_data, send_type, channel) + else: + Steam.sendP2PPacket(this_target, this_data, send_type, channel) + + +func read_p2p_packet() -> void: + var packet_size: int = Steam.getAvailableP2PPacketSize() + + if packet_size > 0: + var this_packet: Dictionary = Steam.readP2PPacket(packet_size, 0) + var packet_sender: int = this_packet["remote_steam_id"] + var packet_code: PackedByteArray = this_packet["data"] + + var read_data: Dictionary = bytes_to_var(packet_code) + + if read_data.has("message"): + match read_data["message"]: + "handshake": + GameConsole.print_line("Handshake from: " + read_data["username"]) + get_lobby_members() + "chat": + GameConsole.print_line(read_data["username"] + ": " + read_data["message"]) + _: + GameConsole.print_line("Unknown message: " + read_data["message"]) \ No newline at end of file diff --git a/assets/core/networking/network_manager.tscn b/assets/core/networking/network_manager.tscn new file mode 100644 index 0000000..79e8d8b --- /dev/null +++ b/assets/core/networking/network_manager.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bpmn7t87tfk7f"] + +[ext_resource type="Script" path="res://assets/core/networking/NetworkManager.gd" id="1_6essl"] + +[node name="NetworkManager" type="Node"] +script = ExtResource("1_6essl") diff --git a/assets/core/networking/network_test_ui.gd b/assets/core/networking/network_test_ui.gd new file mode 100644 index 0000000..b3f39f0 --- /dev/null +++ b/assets/core/networking/network_test_ui.gd @@ -0,0 +1,41 @@ +extends Control + +@export var network_manager: NetworkManager + +@export var host_button: Button +@export var join_button: Button +@export var lobby_id: LineEdit +@export var pfp: TextureRect +@export var username: Label +@export var hosted_lobby_id: Label + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + host_button.button_down.connect(_on_host_button_pressed) + join_button.button_down.connect(_on_join_button_pressed) + + + username.text = network_manager.username + + if network_manager.avatar_texture == null: + GameConsole.log_error("Avatar texture is null!") + get_tree().create_timer(0.005).timeout.connect( + func(): + pfp.texture = network_manager.avatar_texture + ) + else: + pfp.texture = network_manager.avatar_texture + + +func _on_host_button_pressed() -> void: + network_manager.create_lobby() + get_tree().create_timer(0.5).timeout.connect( + func(): + hosted_lobby_id.text = ("Lobby hosted: " + str(network_manager.lobby_id)) + ) + + hosted_lobby_id.visible = true + + +func _on_join_button_pressed() -> void: + network_manager.join_lobby(int(lobby_id.text)) diff --git a/assets/core/networking/test-lobby.tscn b/assets/core/networking/test-lobby.tscn new file mode 100644 index 0000000..f8626f5 --- /dev/null +++ b/assets/core/networking/test-lobby.tscn @@ -0,0 +1,81 @@ +[gd_scene load_steps=4 format=3 uid="uid://dfa8n5rw2qpfd"] + +[ext_resource type="PackedScene" uid="uid://bpmn7t87tfk7f" path="res://assets/core/networking/network_manager.tscn" id="1_i1w5w"] +[ext_resource type="Script" path="res://assets/core/networking/network_test_ui.gd" id="2_rbs4r"] +[ext_resource type="Texture2D" uid="uid://fwub8fvl2u4i" path="res://addons/ingameconsole/ps1hagrid.png" id="3_co2ix"] + +[node name="Test-lobby" type="CanvasLayer"] + +[node name="NetworkManager" parent="." instance=ExtResource("1_i1w5w")] + +[node name="UiRoot" type="Control" parent="." node_paths=PackedStringArray("network_manager", "host_button", "join_button", "lobby_id", "pfp", "username", "hosted_lobby_id")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2_rbs4r") +network_manager = NodePath("../NetworkManager") +host_button = NodePath("Panel/HBoxContainer/VBoxContainer/Host") +join_button = NodePath("Panel/HBoxContainer/VBoxContainer/Join") +lobby_id = NodePath("Panel/HBoxContainer/VBoxContainer/LobbyID") +pfp = NodePath("Panel/HBoxContainer/VBoxContainer2/HBoxContainer/PFP") +username = NodePath("Panel/HBoxContainer/VBoxContainer2/HBoxContainer/Username") +hosted_lobby_id = NodePath("Panel/HBoxContainer/VBoxContainer/HostedLobbyID") + +[node name="Panel" type="Panel" parent="UiRoot"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="UiRoot/Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="UiRoot/Panel/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Host" type="Button" parent="UiRoot/Panel/HBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Host" + +[node name="Join" type="Button" parent="UiRoot/Panel/HBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Join" + +[node name="LobbyID" type="LineEdit" parent="UiRoot/Panel/HBoxContainer/VBoxContainer"] +layout_mode = 2 +placeholder_text = "Lobby ID" + +[node name="HostedLobbyID" type="Label" parent="UiRoot/Panel/HBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Lobby hosted: " + +[node name="VBoxContainer2" type="VBoxContainer" parent="UiRoot/Panel/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="UiRoot/Panel/HBoxContainer/VBoxContainer2"] +layout_mode = 2 + +[node name="PFP" type="TextureRect" parent="UiRoot/Panel/HBoxContainer/VBoxContainer2/HBoxContainer"] +custom_minimum_size = Vector2(128, 128) +layout_mode = 2 +size_flags_horizontal = 4 +texture = ExtResource("3_co2ix") +expand_mode = 1 + +[node name="Username" type="Label" parent="UiRoot/Panel/HBoxContainer/VBoxContainer2/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +text = "username"