diff --git a/README.md b/README.md index e306533..bccc76d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # Lobby Cliet -> **Note:** Meant to be exported as a pck and loaded by game-shell [game-shell](https://code.itdominator.com/itdominator/game-shell). +> **Note:** Meant to be exported as a pck and loaded by the [game-shell](https://code.itdominator.com/itdominator/game-shell). diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..c8ec29c --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,43 @@ +[preset.0] + +name="Lobby Client" +platform="Linux" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="resources" +export_files=PackedStringArray("res://scenes/screens/lobby_screen/lobby.gd", "res://scenes/screens/lobby_screen/lobby.tscn", "res://scenes/screens/lobby_screen/lobby_base.gd", "res://scenes/screens/lobby_screen/lobby_client.gd", "res://scenes/screens/lobby_screen/match_entry.gd", "res://scenes/screens/lobby_screen/match_entry.tscn", "res://scenes/ui/lobby/lobby_ui.tscn") +include_filter="" +exclude_filter="globals/*,scripts/*" +export_path="../../builds/desktop/shell-test/modules/lobby-client.x86_64" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +shader_baker/enabled=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" diff --git a/globals/globals.gd b/globals/globals.gd new file mode 100644 index 0000000..a691949 --- /dev/null +++ b/globals/globals.gd @@ -0,0 +1,15 @@ +extends Node + + +var game_data: GameData +var multiplayer_data: MultiplayerData + + +func cache_invalidate_load_resource(target: String) -> Resource: + # NOTE: Deep replace version b/c we don't want to use an autoloads + # that does this replasce before any nodes actually load. + ResourceLoader.load(target, "", ResourceLoader.CACHE_MODE_REPLACE_DEEP) + + # NOTE: Let prior call propigate naturally. (Unsure when...) + # Enforce new script/node from pck called here. + return ResourceLoader.load(target, "", ResourceLoader.CACHE_MODE_IGNORE_DEEP) diff --git a/globals/globals.gd.uid b/globals/globals.gd.uid new file mode 100644 index 0000000..5d3f638 --- /dev/null +++ b/globals/globals.gd.uid @@ -0,0 +1 @@ +uid://biu64hvcxb1su diff --git a/globals/message_bus.gd b/globals/message_bus.gd new file mode 100644 index 0000000..6a6eac9 --- /dev/null +++ b/globals/message_bus.gd @@ -0,0 +1,68 @@ +extends Node + + +var message_types: Dictionary[String, Array] = {} + + +func subscribe(id: String, callback: Callable) -> void: + if not message_types.has(id): + message_types.set(id, Array()) + + message_types.get(id).append(callback) + + +func clear_voided_listeners() -> void: + for mtype in message_types.keys(): + var mlisteners: Array = message_types.get(mtype) + for mlistener in mlisteners: + if mlistener.is_valid(): continue + mlisteners.erase(mlistener) + + +func emit_local(id: String, data: Variant, expand: bool = false) -> void: + emit_propagation(id, data, expand) + + +# TODO: Need to actually validate client request. Also, message bus is more +# about server talking to clients than clients to server. I.E, reducing +# the number of needed @rpc method decorator bindings. +@rpc("any_peer", "call_remote", "reliable") +func emit_request(id: String, data: Variant, expand: bool = false) -> void: + if not multiplayer.is_server(): return + if not message_types.has(id): + push_error("'%s' not an existing message type to handle...", [id]) + return + + emit_propagation(id, data, expand) + +@rpc("authority", "call_local", "reliable") +func emit_propagation(id: String, data: Variant, expand: bool = false) -> void: + if not message_types.has(id): + push_error("'%s' not an existing message type to handle...", [id]) + return + + for callback: Callable in message_types.get(id): + if data == null: + callback.call() + continue + + if not expand: + callback.call(data) + else: + callback.callv(data) + +@rpc("authority", "call_remote", "reliable") +func emit_propagation_clients_only(id: String, data: Variant, expand: bool = false) -> void: + if not message_types.has(id): + push_error("'%s' not an existing message type to handle...", [id]) + return + + for callback: Callable in message_types.get(id): + if data == null: + callback.call() + continue + + if not expand: + callback.call(data) + else: + callback.callv(data) diff --git a/globals/message_bus.gd.uid b/globals/message_bus.gd.uid new file mode 100644 index 0000000..8c3abe4 --- /dev/null +++ b/globals/message_bus.gd.uid @@ -0,0 +1 @@ +uid://dgkbelaqw2a2g diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..4ba8ca2 --- /dev/null +++ b/icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..ec18da4 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dsiurmg6lea5e" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/lobby_client.tscn b/lobby_client.tscn new file mode 100644 index 0000000..e8cbe48 --- /dev/null +++ b/lobby_client.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=3 format=3 uid="uid://djc2mihyhpoko"] + +[ext_resource type="PackedScene" uid="uid://dy51wny4x53f4" path="res://scripts/data/data_bridge.tscn" id="1_mqgqt"] +[ext_resource type="PackedScene" uid="uid://dkes5cdu45q6e" path="res://scenes/screens/lobby_screen/lobby.tscn" id="2_cirtq"] + +[node name="lobby_client" type="Node"] + +[node name="data_bridge" parent="." instance=ExtResource("1_mqgqt")] + +[node name="lobby" parent="." instance=ExtResource("2_cirtq")] diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..e5370a2 --- /dev/null +++ b/project.godot @@ -0,0 +1,80 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Lobby Client" +config/description="A billiards game with many game mode options plus the ability to add more." +run/main_scene="uid://djc2mihyhpoko" +run/print_header=false +config/features=PackedStringArray("4.5", "Forward Plus") +run/max_fps=30 +boot_splash/bg_color=Color(0, 0, 0, 1) +boot_splash/image="uid://cwqth1sgffu3c" +config/icon="res://icon.svg" +boot_splash/minimum_display_time=2500 + +[autoload] + +Globals="*res://globals/globals.gd" +MessageBus="*res://globals/message_bus.gd" + +[editor] + +movie_writer/fps=30 +naming/node_name_casing=2 +naming/script_name_casing=2 + +[input] + +aim_pulse={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} +aim_release={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} +aim_rotate_left={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +aim_rotate_right={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +aim_incline={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +aim_decline={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + +[physics] + +3d/physics_engine="Jolt Physics" +jolt_physics_3d/simulation/velocity_steps=20 +jolt_physics_3d/simulation/position_steps=20 +3d/simulation/position_steps=10 +3d/simulation/velocity_steps=10 diff --git a/scenes/screens/lobby_screen/lobby.gd b/scenes/screens/lobby_screen/lobby.gd new file mode 100644 index 0000000..127178d --- /dev/null +++ b/scenes/screens/lobby_screen/lobby.gd @@ -0,0 +1,112 @@ +class_name Lobby extends LobbyBase + + +func _ready() -> void: + setup_signals() + + host_address_input.grab_focus() + +func _exit_tree() -> void: + # TODO: Maybe no longer needed... + if Globals.multiplayer_data.is_multiplayer_active: return + client._do_wait_close_connection() + + +func setup_signals() -> void: + MessageBus.subscribe("lobby_wait_for_connection", client._do_wait_for_connection) + MessageBus.subscribe("lobby_close_connection", client._do_wait_close_connection) + + MessageBus.subscribe("receive_match_list", _on_receive_match_list) + MessageBus.subscribe("match_list_activate_entry", _on_match_list_entry_activated) + MessageBus.subscribe("match_list_add_entry", _on_match_list_entry_added) + MessageBus.subscribe("match_list_remove_entry", _on_match_list_entry_removed) + MessageBus.subscribe("match_list_entry_load_match", _on_match_list_entry_load_match) + + +func _server_host_started() -> void: + host_connect_bttn.visible = false + +# NOTE: 'matches_list' can be empty; so long as we get the call from the +# server we know we are connected and thus hide respective UI elements. +func _on_receive_match_list(matches_list: Array) -> void: + push_warning("Client Joined Host: Full Match List Recieved...\n", matches_list) + + match_create_vbox.visible = true + search_vbox.visible = true + host_disconnect_bttn.visible = true + host_connect_bttn.visible = false + + for match_entry in matches_list: + _create_match_entry(match_entry) + + client.lobby_fully_connected = true + new_match_name_input.grab_focus() + +func _on_match_list_entry_activated(match_id: String) -> void: + for match_entry in match_list.get_children(): + match_entry.join_bttn.visible = false + if not match_entry.match_id == match_id: continue + + active_match = match_entry + match_entry.join_bttn.text = "Delete" + match_entry.join_bttn.visible = true + match_create_vbox.visible = false + match_search_input.visible = false + match_search_bttn.visible = false + + match_entry.join_bttn.pressed.disconnect(_join_match) + match_entry.join_bttn.pressed.connect( + _delete_match.bind(match_entry.match_id) + ) + +func _on_match_list_entry_added(match_entry: Dictionary) -> void: + push_warning("Client: Added Match List Entry...", match_entry) + _create_match_entry(match_entry) + +func _on_match_list_entry_removed(match_id: String) -> void: + push_warning("Client: Removed Match List Entry...", match_id) + + if active_match && (match_id == active_match.match_id): + active_match = null + match_create_vbox.visible = true + match_search_input.visible = true + match_search_bttn.visible = false + new_match_name_input.grab_focus() + + for match_entry in match_list.get_children(): + if not active_match: + match_entry.join_bttn.visible = true + + if not match_id == match_entry.match_id: continue + match_entry.queue_free() + +func _on_match_list_entry_load_match(match_entry: Dictionary, is_player_1: bool) -> void: + push_warning( + "Client: Loading Game Match...\nMatch Data: ", match_entry, "\nis_player_1: ", is_player_1 + ) + + Globals.multiplayer_data.set_multiplayer_active() + Globals.game_data.set_game_mode(match_entry.type) + Globals.game_data.set_player_id(is_player_1) + Globals.game_state.set_game_scene("res://scenes/game.tscn") + +func _create_match_entry(match_entry: Dictionary) -> void: + var container = MATCH_ENTRY.instantiate() + match_list.add_child(container) + + container.name_lbl.text = match_entry.name + container.type_lbl.text = match_entry.type + container.match_id = match_entry.match_id + + container.join_bttn.pressed.connect( + _join_match.bind(match_entry.match_id) + ) + + if active_match: + container.join_bttn.visible = false + +func _join_match(match_id: String) -> void: + MessageBus.emit_request.rpc_id(1, "match_list_join_entry", match_id) + +func _delete_match(match_id: String) -> void: + MessageBus.emit_request.rpc_id(1, "match_list_remove_entry", match_id) diff --git a/scenes/screens/lobby_screen/lobby.gd.uid b/scenes/screens/lobby_screen/lobby.gd.uid new file mode 100644 index 0000000..bc1baf7 --- /dev/null +++ b/scenes/screens/lobby_screen/lobby.gd.uid @@ -0,0 +1 @@ +uid://dk7afnf7ol7dy diff --git a/scenes/screens/lobby_screen/lobby.tscn b/scenes/screens/lobby_screen/lobby.tscn new file mode 100644 index 0000000..c05a6c4 --- /dev/null +++ b/scenes/screens/lobby_screen/lobby.tscn @@ -0,0 +1,42 @@ +[gd_scene load_steps=4 format=3 uid="uid://dkes5cdu45q6e"] + +[ext_resource type="Script" uid="uid://dk7afnf7ol7dy" path="res://scenes/screens/lobby_screen/lobby.gd" id="1_hhiik"] +[ext_resource type="PackedScene" uid="uid://dg7pgj6i5pk8p" path="res://scenes/ui/lobby/lobby_ui.tscn" id="2_lqt48"] +[ext_resource type="Script" uid="uid://dv7j35h2ngiq7" path="res://scenes/screens/lobby_screen/lobby_client.gd" id="4_glu7g"] + +[node name="lobby" type="Node"] +script = ExtResource("1_hhiik") + +[node name="lobby_ui" parent="." instance=ExtResource("2_lqt48")] + +[node name="host_address_input" parent="lobby_ui/body/left_body/server_host_hbox" index="1"] +context_menu_enabled = false +select_all_on_focus = true + +[node name="host_port_range" parent="lobby_ui/body/left_body/server_host_hbox" index="2"] +select_all_on_focus = true + +[node name="match_search_input" parent="lobby_ui/body/left_body/search_vbox/search_hbox" index="0"] +context_menu_enabled = false + +[node name="right_body" parent="lobby_ui/body" index="1"] +visible = false + +[node name="new_match_name_input" parent="lobby_ui/body/right_body/create_match_hbox" index="0"] +context_menu_enabled = false +select_all_on_focus = true + +[node name="client" type="Node" parent="."] +script = ExtResource("4_glu7g") + +[connection signal="pressed" from="lobby_ui/body/left_body/server_host_hbox/home_bttn" to="." method="_on_home_bttn_pressed"] +[connection signal="pressed" from="lobby_ui/body/left_body/server_host_hbox/host_connect_bttn" to="." method="_on_host_connect_bttn_pressed"] +[connection signal="pressed" from="lobby_ui/body/left_body/server_host_hbox/host_disconnect_bttn" to="." method="_on_host_disconnect_bttn_pressed"] +[connection signal="pressed" from="lobby_ui/body/left_body/search_vbox/search_hbox/match_search_bttn" to="." method="_on_match_search_bttn_pressed"] +[connection signal="pressed" from="lobby_ui/body/right_body/create_match_hbox/match_create_bttn" to="." method="_on_match_create_bttn_pressed"] +[connection signal="pressed" from="lobby_ui/body/right_body/vbox/8ball" to="." method="_on_mode_bttn_pressed" flags=18] +[connection signal="pressed" from="lobby_ui/body/right_body/vbox/9ball" to="." method="_on_mode_bttn_pressed" flags=18] +[connection signal="pressed" from="lobby_ui/body/right_body/vbox/snooker" to="." method="_on_mode_bttn_pressed" flags=18] +[connection signal="pressed" from="lobby_ui/body/right_body/vbox/freeball" to="." method="_on_mode_bttn_pressed" flags=18] + +[editable path="lobby_ui"] diff --git a/scenes/screens/lobby_screen/lobby_base.gd b/scenes/screens/lobby_screen/lobby_base.gd new file mode 100644 index 0000000..6aeeb95 --- /dev/null +++ b/scenes/screens/lobby_screen/lobby_base.gd @@ -0,0 +1,62 @@ +class_name LobbyBase extends Node + + +const MATCH_ENTRY = preload("res://scenes/screens/lobby_screen/match_entry.tscn") + +@onready var host_address_input: LineEdit = $lobby_ui/body/left_body/server_host_hbox/host_address_input +@onready var host_port_range: SpinBox = $lobby_ui/body/left_body/server_host_hbox/host_port_range +@onready var host_connect_bttn: Button = $lobby_ui/body/left_body/server_host_hbox/host_connect_bttn +@onready var host_disconnect_bttn: Button = $lobby_ui/body/left_body/server_host_hbox/host_disconnect_bttn + +@onready var search_vbox: VBoxContainer = $lobby_ui/body/left_body/search_vbox +@onready var match_search_input: LineEdit = $lobby_ui/body/left_body/search_vbox/search_hbox/match_search_input +@onready var match_search_bttn: Button = $lobby_ui/body/left_body/search_vbox/search_hbox/match_search_bttn +@onready var match_list: VBoxContainer = $lobby_ui/body/left_body/search_vbox/scroll_container/match_list + +@onready var match_create_vbox: VBoxContainer = $lobby_ui/body/right_body +@onready var new_match_name_input: LineEdit = $lobby_ui/body/right_body/create_match_hbox/new_match_name_input + +@onready var client: LobbyClient = $client + +var active_mode: String = "" +var active_match: HBoxContainer = null + + +func _on_home_bttn_pressed() -> void: + Globals.game_state.set_game_scene( + "res://scenes/screens/start_screen/start.tscn" + ) + +func _on_host_connect_bttn_pressed() -> void: + Globals.game_data.game_address = host_address_input.text + Globals.game_data.game_port = int(host_port_range.value) + client.start_client( + Globals.game_data.game_address, Globals.game_data.game_port + ) + +func _on_host_disconnect_bttn_pressed() -> void: + match_create_vbox.visible = false + search_vbox.visible = false + host_disconnect_bttn.visible = false + host_connect_bttn.visible = true + + for match_block in match_list.get_children(): + match_block.queue_free() + + client.close_connection() + +func _on_match_search_bttn_pressed() -> void: + pass + +func _on_match_create_bttn_pressed() -> void: + if active_match: return + + var match_entry: Dictionary = { + "name": new_match_name_input.text, + "type": active_mode + } + + MessageBus.emit_request.rpc("match_list_add_entry", match_entry) + +func _on_mode_bttn_pressed(button: Button) -> void: + active_mode = button.name.to_lower() diff --git a/scenes/screens/lobby_screen/lobby_base.gd.uid b/scenes/screens/lobby_screen/lobby_base.gd.uid new file mode 100644 index 0000000..72c884f --- /dev/null +++ b/scenes/screens/lobby_screen/lobby_base.gd.uid @@ -0,0 +1 @@ +uid://5tvhtoe6fdu1 diff --git a/scenes/screens/lobby_screen/lobby_client.gd b/scenes/screens/lobby_screen/lobby_client.gd new file mode 100644 index 0000000..9c2c783 --- /dev/null +++ b/scenes/screens/lobby_screen/lobby_client.gd @@ -0,0 +1,35 @@ +class_name LobbyClient extends ClientNetworking + + +var lobby_fully_connected: bool = false + + +func _ready() -> void: + pass + +func _process(_delta: float) -> void: + pass + +func _do_wait_for_connection() -> void: + _wait_for_connection() + + while not lobby_fully_connected: + await get_tree().process_frame + +func _do_wait_close_connection(): + _wait_close_connection() + + lobby_fully_connected = false + + +func client_connected_to_server() -> void: + push_warning("Connected to server!") + + +func client_disconnected_from_server() -> void: + push_warning("Disconnected from, server...") + +func client_connection_failed_to_server() -> void: + push_warning("Connection failed!") + + unset_client() diff --git a/scenes/screens/lobby_screen/lobby_client.gd.uid b/scenes/screens/lobby_screen/lobby_client.gd.uid new file mode 100644 index 0000000..8f15cec --- /dev/null +++ b/scenes/screens/lobby_screen/lobby_client.gd.uid @@ -0,0 +1 @@ +uid://dv7j35h2ngiq7 diff --git a/scenes/screens/lobby_screen/match_entry.gd b/scenes/screens/lobby_screen/match_entry.gd new file mode 100644 index 0000000..3d40826 --- /dev/null +++ b/scenes/screens/lobby_screen/match_entry.gd @@ -0,0 +1,8 @@ +class_name MatchEntry extends HBoxContainer + + +@onready var name_lbl: Label = $name_lbl +@onready var type_lbl: Label = $type_lbl +@onready var join_bttn: Button = $join_bttn + +var match_id: String = "" diff --git a/scenes/screens/lobby_screen/match_entry.gd.uid b/scenes/screens/lobby_screen/match_entry.gd.uid new file mode 100644 index 0000000..c1ecc7c --- /dev/null +++ b/scenes/screens/lobby_screen/match_entry.gd.uid @@ -0,0 +1 @@ +uid://ck663xy8rqumo diff --git a/scenes/screens/lobby_screen/match_entry.tscn b/scenes/screens/lobby_screen/match_entry.tscn new file mode 100644 index 0000000..6c1985d --- /dev/null +++ b/scenes/screens/lobby_screen/match_entry.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=2 format=3 uid="uid://b5b18vkfe3caa"] + +[ext_resource type="Script" uid="uid://ck663xy8rqumo" path="res://scenes/screens/lobby_screen/match_entry.gd" id="1_kw53p"] + +[node name="match_entry" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_kw53p") + +[node name="name_lbl" type="Label" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="type_lbl" type="Label" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="join_bttn" type="Button" parent="."] +layout_mode = 2 +text = "Join" diff --git a/scenes/ui/lobby/lobby_ui.tscn b/scenes/ui/lobby/lobby_ui.tscn new file mode 100644 index 0000000..e6544b1 --- /dev/null +++ b/scenes/ui/lobby/lobby_ui.tscn @@ -0,0 +1,164 @@ +[gd_scene load_steps=3 format=3 uid="uid://dg7pgj6i5pk8p"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e03fk"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.22352941, 0.47058824, 0.23137255, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lqt48"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.84705883, 0.21960784, 0.24705882, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[node name="lobby_ui" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 16.0 +offset_top = 23.0 +offset_right = -19.0 +offset_bottom = -27.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="body" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="left_body" type="VBoxContainer" parent="body"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="server_host_hbox" type="HBoxContainer" parent="body/left_body"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="home_bttn" type="Button" parent="body/left_body/server_host_hbox"] +layout_mode = 2 +text = "Home" + +[node name="host_address_input" type="LineEdit" parent="body/left_body/server_host_hbox"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "127.0.0.1" +placeholder_text = "Server Address: (Eg. 0.0.0.0)" +alignment = 1 +max_length = 15 +emoji_menu_enabled = false +clear_button_enabled = true +caret_blink = true + +[node name="host_port_range" type="SpinBox" parent="body/left_body/server_host_hbox"] +layout_mode = 2 +min_value = 1.0 +max_value = 65535.0 +page = 10.0 +value = 8080.0 +rounded = true +alignment = 1 + +[node name="host_connect_bttn" type="Button" parent="body/left_body/server_host_hbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_styles/normal = SubResource("StyleBoxFlat_e03fk") +text = "Connect" + +[node name="host_disconnect_bttn" type="Button" parent="body/left_body/server_host_hbox"] +visible = false +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_override_styles/normal = SubResource("StyleBoxFlat_lqt48") +text = "Disconnect" + +[node name="search_vbox" type="VBoxContainer" parent="body/left_body"] +visible = false +layout_mode = 2 +size_flags_vertical = 3 + +[node name="search_hbox" type="HBoxContainer" parent="body/left_body/search_vbox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="match_search_input" type="LineEdit" parent="body/left_body/search_vbox/search_hbox"] +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Search Match List... " +max_length = 12 +emoji_menu_enabled = false +clear_button_enabled = true +caret_blink = true + +[node name="match_search_bttn" type="Button" parent="body/left_body/search_vbox/search_hbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Search" + +[node name="scroll_container" type="ScrollContainer" parent="body/left_body/search_vbox"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="match_list" type="VBoxContainer" parent="body/left_body/search_vbox/scroll_container"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="right_body" type="VBoxContainer" parent="body"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="create_match_hbox" type="HBoxContainer" parent="body/right_body"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="new_match_name_input" type="LineEdit" parent="body/right_body/create_match_hbox"] +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "New Match Name..." +max_length = 12 +emoji_menu_enabled = false +clear_button_enabled = true + +[node name="match_create_bttn" type="Button" parent="body/right_body/create_match_hbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Create" + +[node name="vbox" type="VBoxContainer" parent="body/right_body"] +layout_mode = 2 +size_flags_vertical = 3 +alignment = 1 + +[node name="8ball" type="Button" parent="body/right_body/vbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +disabled = true +text = "8-Ball" + +[node name="9ball" type="Button" parent="body/right_body/vbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +disabled = true +text = "9-Ball" + +[node name="snooker" type="Button" parent="body/right_body/vbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +disabled = true +text = "Snooker" + +[node name="freeball" type="Button" parent="body/right_body/vbox"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Free Ball" diff --git a/scripts/client_networking.gd b/scripts/client_networking.gd new file mode 100644 index 0000000..9078f70 --- /dev/null +++ b/scripts/client_networking.gd @@ -0,0 +1,67 @@ +class_name ClientNetworking extends Node + + +var peer: ENetMultiplayerPeer + + +func start_client(address: String = "127.0.0.1", port: int = 8080) -> void: + if peer: return + + push_warning("Client Starting... Address: ", address, " Port: ", port) + # TODO: Add throbber or other indication of connection attempt. + + peer = ENetMultiplayerPeer.new() + peer.create_client(address, port) + + multiplayer.multiplayer_peer = peer + multiplayer.connected_to_server.connect(client_connected_to_server) + multiplayer.server_disconnected.connect(client_disconnected_from_server) + multiplayer.connection_failed.connect(client_connection_failed_to_server) + + +func _wait_for_connection() -> void: + while not peer: + await get_tree().process_frame + + while peer.get_connection_status() != 2: + await get_tree().process_frame + +func _wait_close_connection(): + close_connection() + + while peer: + await get_tree().process_frame + + +func client_connected_to_server() -> void: + assert(false, "This method needs to be overridden...") + +func client_disconnected_from_server() -> void: + assert(false, "This method needs to be overridden...") + + +func client_connection_failed_to_server() -> void: + assert(false, "This method needs to be overridden...") + + +func close_connection() -> void: + if not peer: return + + push_warning("Disconnected from, server...") + + unset_client() + + +func unset_client() -> void: + if not peer: return + if not multiplayer: + peer.close() + peer = null + return + + multiplayer.connected_to_server.disconnect(client_connected_to_server) + multiplayer.connection_failed.disconnect(client_connection_failed_to_server) + multiplayer.server_disconnected.disconnect(client_disconnected_from_server) + + peer.close() + peer = null diff --git a/scripts/client_networking.gd.uid b/scripts/client_networking.gd.uid new file mode 100644 index 0000000..be0bb54 --- /dev/null +++ b/scripts/client_networking.gd.uid @@ -0,0 +1 @@ +uid://ci57tiuqsx1jp diff --git a/scripts/data/data_bridge.tscn b/scripts/data/data_bridge.tscn new file mode 100644 index 0000000..1644c8c --- /dev/null +++ b/scripts/data/data_bridge.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=3 format=3 uid="uid://dy51wny4x53f4"] + +[ext_resource type="Script" uid="uid://52eithlmedm3" path="res://scripts/data/game_data.gd" id="1_hulqm"] +[ext_resource type="Script" uid="uid://bs4vblwa3u5jq" path="res://scripts/data/multiplayer_data.gd" id="4_setx2"] + +[node name="data_bridge" type="Node"] + +[node name="game_data" type="Node" parent="."] +script = ExtResource("1_hulqm") + +[node name="multiplayer_data" type="Node" parent="."] +script = ExtResource("4_setx2") diff --git a/scripts/data/game_data.gd b/scripts/data/game_data.gd new file mode 100644 index 0000000..ba8f8e6 --- /dev/null +++ b/scripts/data/game_data.gd @@ -0,0 +1,34 @@ +class_name GameData extends Node + + +var client1: int +var client2: int +var player_id: String = "" + +var game_address: String = "0.0.0.0" +var game_port: int = 8080 +var game_mode: String = "" + +enum PlayerType { + Player1, + Player2 +} + + +func _init() -> void: + Globals.game_data = self + +@rpc("authority", "reliable") +func set_game_address(address: String) -> void: + game_address = address + +@rpc("authority", "reliable") +func set_game_port(port: String) -> void: + game_port = int(port) + + +func set_game_mode(_mode: String) -> void: + pass + +func set_player_id(is_player_1: bool) -> void: + player_id = "Player 1" if is_player_1 else "Player 2" diff --git a/scripts/data/game_data.gd.uid b/scripts/data/game_data.gd.uid new file mode 100644 index 0000000..98a4e8a --- /dev/null +++ b/scripts/data/game_data.gd.uid @@ -0,0 +1 @@ +uid://52eithlmedm3 diff --git a/scripts/data/game_state.gd b/scripts/data/game_state.gd new file mode 100644 index 0000000..6fba066 --- /dev/null +++ b/scripts/data/game_state.gd @@ -0,0 +1,63 @@ +class_name GameState extends Node + + +@warning_ignore_start("unused_signal") +signal update_cue_cam_position + +signal balls_stopped_moving +signal white_ball_sunk +signal white_ball_needs_hard_reset +signal reload_scene +signal process_ball +signal reap_ball +@warning_ignore_restore("unused_signal") + + +func _init() -> void: + Globals.game_state = self + +func load_game_scene(scene_str: String) -> void: + var scene = load(scene_str).instantiate() + var target = get_tree().root.get_node_or_null("shell/scene") + + if not target: + target = get_tree().root.get_node("game_core/scene") + + target.add_child(scene) + +func set_game_scene(scene_str: String) -> void: + var scene = load(scene_str).instantiate() + var target = get_tree().root.get_node_or_null("shell/scene") + + if not target: + target = get_tree().root.get_node("game_core/scene") + + if "game" in scene_str: + scene.name = "game" + elif "lobby" in scene_str: + scene.name = "lobby_screen" + elif "start" in scene_str: + scene.name = "start_screen" + + clear_game_scene(target) + + target.add_child(scene) + +func clear_game_scene(container: Node) -> void: + for child in container.get_children(): + child.queue_free() + + await get_tree().process_frame + +func switch_active_player() -> void: + # TODO: Move away from using client ID or mask it with a map ID + if Globals.multiplayer_data.current_turn_player_id == Globals.game_data.client1: + Globals.multiplayer_data.rpc( + "set_active_player_id", + Globals.game_data.client2 + ) + elif Globals.multiplayer_data.current_turn_player_id == Globals.game_data.client2: + Globals.multiplayer_data.rpc( + "set_active_player_id", + Globals.game_data.client1 + ) diff --git a/scripts/data/game_state.gd.uid b/scripts/data/game_state.gd.uid new file mode 100644 index 0000000..da566e6 --- /dev/null +++ b/scripts/data/game_state.gd.uid @@ -0,0 +1 @@ +uid://rol2c8o8q2ty diff --git a/scripts/data/multiplayer_data.gd b/scripts/data/multiplayer_data.gd new file mode 100644 index 0000000..050aeb7 --- /dev/null +++ b/scripts/data/multiplayer_data.gd @@ -0,0 +1,16 @@ +class_name MultiplayerData extends Node + + +var is_multiplayer_active: bool = false + + +func _init() -> void: + Globals.multiplayer_data = self + +@rpc("authority", "call_local", "reliable") +func set_multiplayer_active() -> void: + is_multiplayer_active = true + +@rpc("authority", "call_local", "reliable") +func set_active_player_id(_id: int) -> void: + pass diff --git a/scripts/data/multiplayer_data.gd.uid b/scripts/data/multiplayer_data.gd.uid new file mode 100644 index 0000000..8634c07 --- /dev/null +++ b/scripts/data/multiplayer_data.gd.uid @@ -0,0 +1 @@ +uid://bs4vblwa3u5jq diff --git a/scripts/server_networking.gd b/scripts/server_networking.gd new file mode 100644 index 0000000..df23922 --- /dev/null +++ b/scripts/server_networking.gd @@ -0,0 +1,47 @@ +class_name ServerNetworking extends Node + + +var peer: ENetMultiplayerPeer + + +func _ready() -> void: + pass + + +func start_server(address: String = "0.0.0.0", port: int = 8080) -> void: + if peer: return + + push_warning("Server Starting... Address: ", address, " Port: ", port) + + peer = ENetMultiplayerPeer.new() + peer.set_bind_ip(address) + + var state = peer.create_server(port) + match state: + OK: + push_warning("Server Started: ", state) + + multiplayer.multiplayer_peer = peer + multiplayer.peer_connected.connect(server_client_connected) + multiplayer.peer_disconnected.connect(server_client_disconnected) + ERR_ALREADY_IN_USE: + peer.close() + peer = null + start_server(address, port) + ERR_CANT_CREATE: + push_warning("Couldn't create server...") + +func close_connection() -> void: + push_warning("Server Closed...") + + multiplayer.peer_connected.disconnect(server_client_connected) + multiplayer.peer_disconnected.disconnect(server_client_disconnected) + + peer.close() + peer = null + +func server_client_connected(_id: int) -> void: + assert(false, "This method needs to be overridden...") + +func server_client_disconnected(_id: int) -> void: + assert(false, "This method needs to be overridden...") diff --git a/scripts/server_networking.gd.uid b/scripts/server_networking.gd.uid new file mode 100644 index 0000000..c819b58 --- /dev/null +++ b/scripts/server_networking.gd.uid @@ -0,0 +1 @@ +uid://bgnr36m8qvbx3 diff --git a/splash.png b/splash.png new file mode 100644 index 0000000..3091fd6 Binary files /dev/null and b/splash.png differ diff --git a/splash.png.import b/splash.png.import new file mode 100644 index 0000000..3187d44 --- /dev/null +++ b/splash.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7t31fn00rvc1" +path="res://.godot/imported/splash.png-929ed8a00b89ba36c51789452f874c77.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://splash.png" +dest_files=["res://.godot/imported/splash.png-929ed8a00b89ba36c51789452f874c77.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1