diff --git a/chat_panel.gd b/chat_panel.gd index 3009a29..a9cdbc3 100644 --- a/chat_panel.gd +++ b/chat_panel.gd @@ -33,9 +33,6 @@ func _send_chat_message() -> void: message_sent.emit(message_input.text.strip_edges()) message_input.text = "" -func _on_web_client_user_message_received(sender: String, message: String) -> void: - put_chat_message(sender, message) - func _on_message_input_text_changed(new_text: String) -> void: send_message_button.disabled = new_text.is_empty() @@ -45,3 +42,12 @@ func _on_message_input_gui_input(event: InputEvent) -> void: func _on_send_message_button_pressed() -> void: _send_chat_message() + + +func _on_web_client_connection_status_changed(new_status: WebClient.ConnectionStatus) -> void: + if new_status == WebClient.ConnectionStatus.JOINED: + message_input.editable = true + send_message_button.disabled = false + else: + message_input.editable = false + send_message_button.disabled = true diff --git a/components/client_interface.gd b/components/client_interface.gd index dadfeaa..e7c89d2 100644 --- a/components/client_interface.gd +++ b/components/client_interface.gd @@ -1,16 +1,12 @@ class_name ClientInterface extends Control -@export var client: WebClientTCP +@export var client: WebClient -@export var connect_button: Button @export var create_lobby_button: Button @export var join_lobby_button: Button @export var leave_lobby_button: Button -@export var chat_message_input: LineEdit -@export var send_chat_message_button: Button - @export var connected_players_label: Label @export var name_input: LineEdit @@ -25,9 +21,6 @@ func _write_line(text: String) -> void: func _on_create_lobby_button_pressed() -> void: client.create_lobby(name_input.text.strip_edges()) -func _on_connect_button_pressed() -> void: - client.connect_to_server($MarginContainer/VBoxContainer/ServerInputs/URLInput.text) - func _on_join_lobby_button_pressed() -> void: client.join_lobby(lobby_code_input.text.strip_edges(), name_input.text.strip_edges()) @@ -39,7 +32,8 @@ func _on_web_client_tcp_connected_to_server() -> void: _write_line("You are connected to the server") func _on_web_client_tcp_connection_error_occurred() -> void: - connect_button.disabled = false + #connect_button.disabled = false + pass func _on_web_client_tcp_disconnected_from_server() -> void: _write_line("You were disconnected from the server") @@ -62,60 +56,45 @@ func _on_web_client_tcp_client_left_lobby(client_name: String, connected_client_ _write_line("{client_name} left the lobby".format({"client_name": client_name})) _update_connected_players(connected_client_names) -func _on_web_client_tcp_connection_status_changed(new_status: WebClientTCP.ConnectionStatus) -> void: +func _on_web_client_tcp_connection_status_changed(new_status: WebClient.ConnectionStatus) -> void: match new_status: - WebClientTCP.ConnectionStatus.DISCONNECTED: - connect_button.disabled = false + WebClient.ConnectionStatus.DISCONNECTED: create_lobby_button.disabled = true join_lobby_button.disabled = true leave_lobby_button.disabled = true name_input.editable = true lobby_code_input.editable = false lobby_code_input.text = "" - chat_message_input.editable = false - send_chat_message_button.disabled = true - WebClientTCP.ConnectionStatus.ATTEMPTING_CONNECTION: - connect_button.disabled = true + WebClient.ConnectionStatus.ATTEMPTING_CONNECTION: create_lobby_button.disabled = true join_lobby_button.disabled = true leave_lobby_button.disabled = true name_input.editable = true lobby_code_input.editable = false lobby_code_input.text = "" - chat_message_input.editable = false - send_chat_message_button.disabled = true - WebClientTCP.ConnectionStatus.LOBBYLESS: - connect_button.disabled = true + WebClient.ConnectionStatus.LOBBYLESS: create_lobby_button.disabled = false join_lobby_button.disabled = false leave_lobby_button.disabled = true name_input.editable = true lobby_code_input.editable = true lobby_code_input.text = "" - chat_message_input.editable = false - send_chat_message_button.disabled = true - WebClientTCP.ConnectionStatus.ATTEMPTING_JOIN: - connect_button.disabled = true + WebClient.ConnectionStatus.ATTEMPTING_JOIN: create_lobby_button.disabled = true join_lobby_button.disabled = true leave_lobby_button.disabled = true name_input.editable = false lobby_code_input.editable = false - chat_message_input.editable = false - send_chat_message_button.disabled = true - WebClientTCP.ConnectionStatus.JOINED: - connect_button.disabled = true + WebClient.ConnectionStatus.JOINED: create_lobby_button.disabled = true join_lobby_button.disabled = true leave_lobby_button.disabled = false name_input.editable = false lobby_code_input.editable = false - chat_message_input.editable = true - send_chat_message_button.disabled = false func _on_web_client_tcp_received_connection_message(message: String) -> void: _write_line(message) diff --git a/components/web_client.gd b/components/web_client.gd index 9cf081f..297246f 100644 --- a/components/web_client.gd +++ b/components/web_client.gd @@ -1,169 +1,313 @@ class_name WebClient extends Node -@export var name_input: LineEdit -@export var session_code_input: LineEdit -@export var output: Label -@export var create_lobby_button: Button -@export var join_lobby_button: Button -@export var disconnect_lobby_button: Button +## Sender tag for system messages displayed in the chat. +const SENDER_SYSTEM: String = "SYSTEM" -#var _peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() -var _address: String = "127.0.0.1" -var _port: int = 9974 +## Server address to connect to. +## Local: 127.0.0.1:9974 +## Remote: wss://denizk0461.dev/magsrv +const SERVER_ADDRESS: String = "127.0.0.1:9974" -var _udp: PacketPeerUDP = PacketPeerUDP.new() -var _stream := StreamPeerTCP.new() +#var _tcp := StreamPeerTCP.new() +var _socket := WebSocketPeer.new() -var _session_id: String +var _new_status: int = WebSocketPeer.STATE_CLOSED +var _processed_status: int = -1 + +var _lobby_id: String var _client_id: String var _client_name: String -var _is_in_lobby: bool = false +var received_data: Array +var cached_response: String -const MSG_CREATE_LOBBY: String = "cr" -const MSG_JOIN_LOBBY: String = "jn" -const MSG_LEAVE_LOBBY: String = "lv" +var _is_first_play: bool = false -const REMOTE_CREATE_SESSION_SUCCESS: String = "c0" -const REMOTE_ERR_CREATE_SESSION: String = "c1" -const REMOTE_JOIN_SESSION_SUCCESS: String = "j0" -const REMOTE_JOIN_SESSION_DUPLICATE: String = "j1" -const REMOTE_JOIN_SESSION_MISSING: String = "j2" -const REMOTE_JOIN_SESSION_FULL: String = "j3" -const REMOTE_EXIT_SESSION: String = "ex" +var _connected_client_ids: Array[String] = [] +var _connected_client_names: Array[String] = [] + + + + +@export var chat_panel: ChatPanel + +signal connected_to_server() +signal connection_error_occurred() +signal disconnected_from_server() + +signal connected_to_lobby(lobby_id: String) +signal disconnected_from_lobby() +signal received_client_id(client_id: String) + +signal client_joined_lobby(client_name: String, connected_client_names: Array[String]) +signal client_left_lobby(client_name: String, connected_client_names: Array[String]) + +signal received_connection_message(message: String) +signal received_chat_message(client_name: String, message: String) + +signal connection_status_changed(new_status: ConnectionStatus) + +# This signal is messy as fuck +signal client_order_received(client_ids: Array[String], client_ids_playing_order: Array[String], client_names: Array[String]) + +signal client_played_card(client_id: String, card_id: int) +signal received_expected_bet(client_id: String, expected_bet: int) +signal new_round_started() +signal cards_received(cards: Dictionary) +signal trump_received(trump_id: int) +signal received_bet_request(disallowed_bet: int, current_round: int) +signal received_win() +signal received_winning_client(client_id: String) +signal notify_play_card_request() +signal reset_first_card() +signal received_first_card(card_id: int) +signal received_played_card(client_id: String, card_id: int) +signal received_play_winner_request() +signal received_end_game_request() +signal request_next_play() +signal request_next_round() + +enum ConnectionStatus { + DISCONNECTED, + ATTEMPTING_CONNECTION, + LOBBYLESS, # == connected + ATTEMPTING_JOIN, + JOINED, +} func _ready() -> void: - _write_output("me client, you jane.") + connect_to_server(SERVER_ADDRESS) -func _process(delta: float) -> void: - if _udp.get_available_packet_count() > 0: - var packet_str = _udp.get_packet().get_string_from_ascii() - print(packet_str) - match packet_str.substr(0, 2): - # Session creation - REMOTE_CREATE_SESSION_SUCCESS: - _write_output("Created and joined lobby successfully.") - _write_output("Lobby code: {session_id}".format({ - "session_id": _session_id - })) - _write_output("Your player code: {client_id}".format({ - "client_id": _client_id - })) - _on_lobby_connected() +func _process(_delta: float) -> void: + _socket.poll() + _new_status = _socket.get_ready_state() + if not _processed_status == _new_status: + match _new_status: + WebSocketPeer.STATE_OPEN: + print("open") + connected_to_server.emit() + connection_status_changed.emit(ConnectionStatus.LOBBYLESS) + WebSocketPeer.STATE_CONNECTING: + print("connecting") + connection_status_changed.emit(ConnectionStatus.ATTEMPTING_CONNECTION) + WebSocketPeer.STATE_CLOSING: + print("closing") + pass + WebSocketPeer.STATE_CLOSED: + print("closed") + connection_status_changed.emit(ConnectionStatus.DISCONNECTED) - REMOTE_ERR_CREATE_SESSION: - _write_output("Could not create lobby.") - _write_output("A lobby with the code {session_id} already exists.".format({ - "session_id": _session_id - })) + _processed_status = _new_status + + if _processed_status == WebSocketPeer.STATE_OPEN: + while _socket.get_available_packet_count(): + cached_response = _socket.get_packet().get_string_from_utf8() + #print(cached_response) + #cached_response += (received_data[1] as PackedByteArray).get_string_from_utf8() + if cached_response.ends_with("}"): + #print(cached_response) + _handle_response(cached_response) + cached_response = "" + +func _listen_for_data() -> void: + while true: + if _socket.get_available_packet_count(): + cached_response = _socket.get_packet().get_string_from_utf8() + print(cached_response) + if cached_response.ends_with("}"): + _handle_response(cached_response) + cached_response = "" + +func _handle_response(response: String) -> void: + var data = JSON.parse_string(response) + match data["response"]: + "create_lobby": + if data["status"] == "ok": + _lobby_id = data["lobby_id"] + _client_id = data["client_id"] + received_client_id.emit.call_deferred(_client_id) + connected_to_lobby.emit.call_deferred(_lobby_id) + connection_status_changed.emit.call_deferred(ConnectionStatus.JOINED) + "join_lobby_response": + _handle_join(data) + "client_joined_lobby": + print(data["connected_client_ids"]) + _connected_client_ids.assign(data["connected_client_ids"]) + _connected_client_names.assign(data["connected_client_names"]) + client_joined_lobby.emit.call_deferred(data["new_client_name"], _connected_client_names) + "leave_lobby_request": + leave_lobby() + "leave_lobby": + if data["status"] == "ok": + client_left_lobby.emit.call_deferred(data["client_name"]) + "client_left_lobby": + _connected_client_ids.assign(data["connected_client_ids"]) + _connected_client_names.assign(data["connected_client_names"]) + client_left_lobby.emit.call_deferred(data["client_name"], _connected_client_names) + + ## A chat message has been received + "receive_chat_message": + # Relay message to be displayed in the chat panel + chat_panel.put_chat_message(data["client_name"], data["message"]) + + "new_round_started": + if data["current_round"] == 1: + var playing_order: Array[String] = [] + playing_order.assign(data["playing_order"]) + client_order_received.emit.call_deferred(playing_order, _connected_client_ids, _connected_client_names) - REMOTE_JOIN_SESSION_SUCCESS: - _write_output("Joined lobby successfully.") - _write_output("Your player code: {client_id}".format({"client_id": _client_id})) - _on_lobby_connected() - - # Session joining - REMOTE_JOIN_SESSION_DUPLICATE: - _write_output("Could not join lobby {session_id}.".format({ - "session_id": _session_id - })) - _write_output("A player with the code {client_id} is already registered in the lobby.".format({ - "client_id": _client_id, - })) - - REMOTE_JOIN_SESSION_MISSING: - _write_output("No lobby exists with the code {session_id}.".format({ - "session_id": _session_id - })) - - REMOTE_JOIN_SESSION_FULL: - _write_output("Could not join lobby {session_id}.".format({ - "session_id": _session_id - })) - _write_output("The lobby has reached the maximum player count.") - - # Session exit - REMOTE_EXIT_SESSION: - _on_lobby_disconnected() + var received_cards = data["cards"] + new_round_started.emit.call_deferred() + cards_received.emit.call_deferred(received_cards) + trump_received.emit.call_deferred(data["trump"]) + "client_submitted_bet": + received_expected_bet.emit.call_deferred( + data["client_id"], + data["bet"], + ) + "request_bet": + received_bet_request.emit.call_deferred(data["disallowed_bet"], data["round"]) + "begin_playing_cards": + # all players know it's time to play + print(data) + _is_first_play = true + reset_first_card.emit.call_deferred() + "request_card": + # individual player asked to play card + notify_play_card_request.emit.call_deferred() + "begin_next_play": + print("beginning next play!") + _is_first_play = true + reset_first_card.emit.call_deferred() + "play_card": + print(data) + #client_played_card.emit.call_deferred(data["client_id"], data["played_card"]) + "determine_play_winner_request": # single card go-around + received_play_winner_request.emit.call_deferred() + "end_game_request": + received_end_game_request.emit.call_deferred() + "client_played_card": + print("RECEIVED FOLLOWING CARD ID: " + str(data["played_card"])) + if _is_first_play: + print("set first card") + received_first_card.emit.call_deferred(data["played_card"]) + if not CardManager.get_card_color(data["played_card"]) == Card.CardColor.WHITE: + _is_first_play = false + received_played_card.emit.call_deferred(data["client_id"], data["played_card"]) + "client_won_play": + if data["client_id"] == _client_id: + received_win.emit.call_deferred() + received_winning_client.emit.call_deferred(data["client_id"]) -func _exit_tree() -> void: - _disconnect_from_lobby() +func _handle_join(data) -> void: + match data["status"]: + "lobby_not_found": + received_connection_message.emit.call_deferred("Could not join; lobby does not exist") + connection_status_changed.emit.call_deferred(ConnectionStatus.LOBBYLESS) + "lobby_full": + received_connection_message.emit.call_deferred("Could not join; lobby is full") + connection_status_changed.emit.call_deferred(ConnectionStatus.LOBBYLESS) + "ok": + _client_id = data["client_id"] + connected_to_lobby.emit.call_deferred(_lobby_id) + received_client_id.emit.call_deferred(_client_id) + connection_status_changed.emit.call_deferred(ConnectionStatus.JOINED) -func _generate_code() -> String: - # use a local randomized RNG to keep the global RNG reproducible - var rng = RandomNumberGenerator.new() - rng.randomize() - var length = 6 - var result = '' - for _n in range(length): - var ascii = rng.randi_range(0, 25) + 65 - result += '%c' % ascii - return result +func _exit_tree() -> void: + disconnect_from_server() -func _on_peer_connected(id: int) -> void: - $Label.text += "connected to id: " + str(id) +func connect_to_server(text: String) -> void: + var err = _socket.connect_to_url(text) + if err != OK: + print("didn't work!") -func _get_client_string(remote_code: String, session_id: String, client_id: String, client_name: String) -> PackedByteArray: - return "{remote_code}:{session_id}:{client_id}:{client_name}".format({ - "remote_code": remote_code, - "session_id": session_id, - "client_id": client_id, - "client_name": client_name, - }).to_utf8_buffer() +func disconnect_from_server() -> void: + _socket.close() + disconnected_from_server.emit() + connection_status_changed.emit(ConnectionStatus.DISCONNECTED) -func _disconnect_from_lobby() -> void: - if _is_in_lobby: - _udp.put_packet(_get_client_string(MSG_LEAVE_LOBBY, _session_id, _client_id, _client_name)) - _udp.close() +func create_lobby(client_name: String) -> void: + _client_name = client_name + + _send_message(TCPMessages.create_lobby(client_name)) -func _write_output(text: String) -> void: - output.text += text + "\n" +func join_lobby(lobby_id: String, client_name: String) -> void: + _lobby_id = lobby_id + _client_name = client_name + + _send_message(TCPMessages.join_lobby(_lobby_id, client_name)) + connection_status_changed.emit.call_deferred(ConnectionStatus.ATTEMPTING_JOIN) -func _on_lobby_connected() -> void: - _is_in_lobby = true - create_lobby_button.disabled = true - join_lobby_button.disabled = true - disconnect_lobby_button.disabled = false +func leave_lobby() -> void: + _send_message(TCPMessages.leave_lobby(_lobby_id, _client_id)) + + _lobby_id = "" + _client_id = "" + _client_name = "" + + disconnected_from_lobby.emit.call_deferred() + connection_status_changed.emit.call_deferred(ConnectionStatus.LOBBYLESS) -func _on_lobby_disconnected() -> void: - _write_output("Connection with lobby {session_id} ended.".format({ - "session_id": _session_id +func send_chat_message(message: String) -> void: + #_send_message(TCPMessages.send_chat_message(_lobby_id, _client_id, message)) + pass + +func start_game() -> void: + _send_message(JSON.stringify({ + "response": "start_game", + "lobby_id": _lobby_id, + "cards": CardManager.get_shuffled_cards(_connected_client_ids, 1), })) - _udp.close() - _is_in_lobby = false - name_input.editable = true - session_code_input.editable = true - session_code_input.text = "" - _session_id = "" - create_lobby_button.disabled = false - join_lobby_button.disabled = false - disconnect_lobby_button.disabled = true -# Create new session -func _on_create_lobby_button_pressed() -> void: - _udp.set_dest_address(_address, _port) - _session_id = _generate_code() - session_code_input.text = _session_id - name_input.editable = false - session_code_input.editable = false - _client_id = _generate_code() - _client_name = name_input.text.strip_edges() - - _udp.put_packet(_get_client_string(MSG_CREATE_LOBBY, _session_id, _client_id, _client_name)) +func start_next_play() -> void: + _send_message(JSON.stringify({ + "response": "start_next_play", + "lobby_id": _lobby_id, + })) -# Connect to existing session -func _on_join_lobby_button_pressed() -> void: - _udp.set_dest_address(_address, _port) - name_input.editable = false - session_code_input.editable = false - - _session_id = session_code_input.text.strip_edges() - _client_id = _generate_code() - _client_name = name_input.text.strip_edges() - - _udp.put_packet(_get_client_string(MSG_JOIN_LOBBY, _session_id, _client_id, _client_name)) +func start_next_round(round_count: int) -> void: + _send_message(JSON.stringify({ + "response": "start_next_round", + "lobby_id": _lobby_id, + "round": round_count, + "cards": CardManager.get_shuffled_cards(_connected_client_ids, round_count), + })) -func _on_disconnect_lobby_button_pressed() -> void: - _disconnect_from_lobby() - _on_lobby_disconnected() +func end_game() -> void: + _send_message(JSON.stringify({ + "response": "end_game", + "lobby_id": _lobby_id, + })) + +func play_card(card_id: int) -> void: + _send_message(JSON.stringify({ + "response": "play_card", + "lobby_id": _lobby_id, + "client_id": _client_id, + "played_card": card_id, + })) + +func submit_bet(bet: int, current_round: int) -> void: + _send_message(JSON.stringify({ + "response": "submit_bet", + "lobby_id": _lobby_id, + "client_id": _client_id, + "round": current_round, + "bet": bet, + })) + +func submit_play_winner(winning_client_id: String) -> void: + _send_message(JSON.stringify({ + "response": "submit_play_winner", + "lobby_id": _lobby_id, + "client_id": winning_client_id, + })) + request_next_play.emit.call_deferred() + #request_next_round.emit.call_deferred() + +func _send_message(message: String) -> void: + _socket.send_text(message) + +func _on_chat_panel_message_sent(message: String) -> void: + _send_message(TCPMessages.send_chat_message(_lobby_id, _client_id, message)) diff --git a/components/web_client.gd.uid b/components/web_client.gd.uid index a1dc6e0..4555df8 100644 --- a/components/web_client.gd.uid +++ b/components/web_client.gd.uid @@ -1 +1 @@ -uid://bfaxxdr7v0emr +uid://p5gvnyhjqe6o diff --git a/components/web_client_tcp.gd b/components/web_client_tcp.gd deleted file mode 100644 index 1f3c536..0000000 --- a/components/web_client_tcp.gd +++ /dev/null @@ -1,311 +0,0 @@ -class_name WebClientTCP -extends Node - -## Sender tag for system messages displayed in the chat -const SENDER_SYSTEM: String = "SYSTEM" - -#var _tcp := StreamPeerTCP.new() -var _socket := WebSocketPeer.new() - -var _new_status: int = WebSocketPeer.STATE_CLOSED -var _processed_status: int = -1 - -var _lobby_id: String -var _client_id: String -var _client_name: String - -var received_data: Array -var cached_response: String - -var _thread: Thread -var _stop_thread: bool = false - -var _is_first_play: bool = false - -var _connected_client_ids: Array[String] = [] -var _connected_client_names: Array[String] = [] - -signal connected_to_server() -signal connection_error_occurred() -signal disconnected_from_server() - -signal connected_to_lobby(lobby_id: String) -signal disconnected_from_lobby() -signal received_client_id(client_id: String) - -signal client_joined_lobby(client_name: String, connected_client_names: Array[String]) -signal client_left_lobby(client_name: String, connected_client_names: Array[String]) - -signal received_connection_message(message: String) -signal received_chat_message(client_name: String, message: String) - -signal connection_status_changed(new_status: ConnectionStatus) - -# This signal is messy as fuck -signal client_order_received(client_ids: Array[String], client_ids_playing_order: Array[String], client_names: Array[String]) - -signal client_played_card(client_id: String, card_id: int) -signal received_expected_bet(client_id: String, expected_bet: int) -signal new_round_started() -signal cards_received(cards: Dictionary) -signal trump_received(trump_id: int) -signal received_bet_request(disallowed_bet: int, current_round: int) -signal received_win() -signal received_winning_client(client_id: String) -signal notify_play_card_request() -signal reset_first_card() -signal received_first_card(card_id: int) -signal received_played_card(client_id: String, card_id: int) -signal received_play_winner_request() -signal received_end_game_request() -signal request_next_play() -signal request_next_round() - -# new signals -## Emitted when a message that needs to be displayed to the player has been received. -signal user_message_received(sender: String, message: String) - -enum ConnectionStatus { - DISCONNECTED, - ATTEMPTING_CONNECTION, - LOBBYLESS, # == connected - ATTEMPTING_JOIN, - JOINED, -} - -func _process(_delta: float) -> void: - _socket.poll() - _new_status = _socket.get_ready_state() - if not _processed_status == _new_status: - match _new_status: - WebSocketPeer.STATE_OPEN: - print("open") - _stop_thread = false - #_thread = Thread.new() - #_thread.start(_listen_for_data) - connected_to_server.emit() - connection_status_changed.emit(ConnectionStatus.LOBBYLESS) - WebSocketPeer.STATE_CONNECTING: - print("connecting") - connection_status_changed.emit(ConnectionStatus.ATTEMPTING_CONNECTION) - WebSocketPeer.STATE_CLOSING: - print("closing") - pass - WebSocketPeer.STATE_CLOSED: - print("closed") - #_clean_up_thread() - connection_status_changed.emit(ConnectionStatus.DISCONNECTED) - - _processed_status = _new_status - - if _processed_status == WebSocketPeer.STATE_OPEN: - while _socket.get_available_packet_count(): - cached_response = _socket.get_packet().get_string_from_utf8() - #print(cached_response) - #cached_response += (received_data[1] as PackedByteArray).get_string_from_utf8() - if cached_response.ends_with("}"): - #print(cached_response) - _handle_response(cached_response) - cached_response = "" - -func _listen_for_data() -> void: - while true: - if _socket.get_available_packet_count(): - cached_response = _socket.get_packet().get_string_from_utf8() - print(cached_response) - if cached_response.ends_with("}"): - _handle_response(cached_response) - cached_response = "" - -func _handle_response(response: String) -> void: - var data = JSON.parse_string(response) - match data["response"]: - "create_lobby": - if data["status"] == "ok": - _lobby_id = data["lobby_id"] - _client_id = data["client_id"] - received_client_id.emit.call_deferred(_client_id) - connected_to_lobby.emit.call_deferred(_lobby_id) - connection_status_changed.emit.call_deferred(ConnectionStatus.JOINED) - "join_lobby_response": - _handle_join(data) - "client_joined_lobby": - print(data["connected_client_ids"]) - _connected_client_ids.assign(data["connected_client_ids"]) - _connected_client_names.assign(data["connected_client_names"]) - client_joined_lobby.emit.call_deferred(data["new_client_name"], _connected_client_names) - "leave_lobby_request": - leave_lobby() - "leave_lobby": - if data["status"] == "ok": - client_left_lobby.emit.call_deferred(data["client_name"]) - "client_left_lobby": - _connected_client_ids.assign(data["connected_client_ids"]) - _connected_client_names.assign(data["connected_client_names"]) - client_left_lobby.emit.call_deferred(data["client_name"], _connected_client_names) - - ## A chat message has been received - "receive_chat_message": - # Relay message to be displayed in the chat panel - user_message_received.emit.call_deferred(data["client_name"], data["message"]) - - "new_round_started": - if data["current_round"] == 1: - var playing_order: Array[String] = [] - playing_order.assign(data["playing_order"]) - client_order_received.emit.call_deferred(playing_order, _connected_client_ids, _connected_client_names) - - var received_cards = data["cards"] - new_round_started.emit.call_deferred() - cards_received.emit.call_deferred(received_cards) - trump_received.emit.call_deferred(data["trump"]) - "client_submitted_bet": - received_expected_bet.emit.call_deferred( - data["client_id"], - data["bet"], - ) - "request_bet": - received_bet_request.emit.call_deferred(data["disallowed_bet"], data["round"]) - "begin_playing_cards": - # all players know it's time to play - print(data) - _is_first_play = true - reset_first_card.emit.call_deferred() - "request_card": - # individual player asked to play card - notify_play_card_request.emit.call_deferred() - "begin_next_play": - print("beginning next play!") - _is_first_play = true - reset_first_card.emit.call_deferred() - "play_card": - print(data) - #client_played_card.emit.call_deferred(data["client_id"], data["played_card"]) - "determine_play_winner_request": # single card go-around - received_play_winner_request.emit.call_deferred() - "end_game_request": - received_end_game_request.emit.call_deferred() - "client_played_card": - print("RECEIVED FOLLOWING CARD ID: " + str(data["played_card"])) - if _is_first_play: - print("set first card") - received_first_card.emit.call_deferred(data["played_card"]) - if not CardManager.get_card_color(data["played_card"]) == Card.CardColor.WHITE: - _is_first_play = false - received_played_card.emit.call_deferred(data["client_id"], data["played_card"]) - "client_won_play": - if data["client_id"] == _client_id: - received_win.emit.call_deferred() - received_winning_client.emit.call_deferred(data["client_id"]) - -func _handle_join(data) -> void: - match data["status"]: - "lobby_not_found": - received_connection_message.emit.call_deferred("Could not join; lobby does not exist") - connection_status_changed.emit.call_deferred(ConnectionStatus.LOBBYLESS) - "lobby_full": - received_connection_message.emit.call_deferred("Could not join; lobby is full") - connection_status_changed.emit.call_deferred(ConnectionStatus.LOBBYLESS) - "ok": - _client_id = data["client_id"] - connected_to_lobby.emit.call_deferred(_lobby_id) - received_client_id.emit.call_deferred(_client_id) - connection_status_changed.emit.call_deferred(ConnectionStatus.JOINED) - -func _exit_tree() -> void: - disconnect_from_server() - -func connect_to_server(text: String) -> void: - var err = _socket.connect_to_url(text) - if err != OK: - print("didn't work!") - -func disconnect_from_server() -> void: - _socket.close() - disconnected_from_server.emit() - connection_status_changed.emit(ConnectionStatus.DISCONNECTED) - -func create_lobby(client_name: String) -> void: - _client_name = client_name - - _send_message(TCPMessages.create_lobby(client_name)) - -func join_lobby(lobby_id: String, client_name: String) -> void: - _lobby_id = lobby_id - _client_name = client_name - - _send_message(TCPMessages.join_lobby(_lobby_id, client_name)) - connection_status_changed.emit.call_deferred(ConnectionStatus.ATTEMPTING_JOIN) - -func leave_lobby() -> void: - _send_message(TCPMessages.leave_lobby(_lobby_id, _client_id)) - - _lobby_id = "" - _client_id = "" - _client_name = "" - - disconnected_from_lobby.emit.call_deferred() - connection_status_changed.emit.call_deferred(ConnectionStatus.LOBBYLESS) - -func send_chat_message(message: String) -> void: - #_send_message(TCPMessages.send_chat_message(_lobby_id, _client_id, message)) - pass - -func start_game() -> void: - _send_message(JSON.stringify({ - "response": "start_game", - "lobby_id": _lobby_id, - "cards": CardManager.get_shuffled_cards(_connected_client_ids, 1), - })) - -func start_next_play() -> void: - _send_message(JSON.stringify({ - "response": "start_next_play", - "lobby_id": _lobby_id, - })) - -func start_next_round(round_count: int) -> void: - _send_message(JSON.stringify({ - "response": "start_next_round", - "lobby_id": _lobby_id, - "round": round_count, - "cards": CardManager.get_shuffled_cards(_connected_client_ids, round_count), - })) - -func end_game() -> void: - _send_message(JSON.stringify({ - "response": "end_game", - "lobby_id": _lobby_id, - })) - -func play_card(card_id: int) -> void: - _send_message(JSON.stringify({ - "response": "play_card", - "lobby_id": _lobby_id, - "client_id": _client_id, - "played_card": card_id, - })) - -func submit_bet(bet: int, current_round: int) -> void: - _send_message(JSON.stringify({ - "response": "submit_bet", - "lobby_id": _lobby_id, - "client_id": _client_id, - "round": current_round, - "bet": bet, - })) - -func submit_play_winner(winning_client_id: String) -> void: - _send_message(JSON.stringify({ - "response": "submit_play_winner", - "lobby_id": _lobby_id, - "client_id": winning_client_id, - })) - request_next_play.emit.call_deferred() - #request_next_round.emit.call_deferred() - -func _send_message(message: String) -> void: - _socket.send_text(message) - -func _on_chat_panel_message_sent(message: String) -> void: - _send_message(TCPMessages.send_chat_message(_lobby_id, _client_id, message)) diff --git a/components/web_client_tcp.gd.uid b/components/web_client_tcp.gd.uid deleted file mode 100644 index 4555df8..0000000 --- a/components/web_client_tcp.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://p5gvnyhjqe6o diff --git a/connection_overlay.gd b/connection_overlay.gd new file mode 100644 index 0000000..c77d3bb --- /dev/null +++ b/connection_overlay.gd @@ -0,0 +1,30 @@ +class_name ConnectionOverlay +extends Control + +@export var status_label: Label +@export_range(1.0, 3.0, 0.1) var fade_out_duration: float = 2.0 + +var _period_count: int = 1 +# Exclusive +var _period_max: int = 4 + +var _is_connected: bool = false + +func _physics_process(delta: float) -> void: + if not _is_connected and Engine.get_physics_frames() % 42 == 0: + status_label.text = "Connecting to server" + ".".repeat(_period_count) + _period_count += 1 + if _period_count == _period_max: + _period_count = 0 + +func _transition_hide() -> void: + _is_connected = true + status_label.text = "Connected!" + create_tween().set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN).tween_property(self, "modulate:a", 0.0, fade_out_duration) + await get_tree().create_timer(fade_out_duration).timeout + hide() + +func _on_web_client_connection_status_changed(new_status: WebClient.ConnectionStatus) -> void: + # Hides screen once connected. This does not take disconnects into consideration! + if new_status == WebClient.ConnectionStatus.LOBBYLESS: + _transition_hide() diff --git a/connection_overlay.gd.uid b/connection_overlay.gd.uid new file mode 100644 index 0000000..ad7a47a --- /dev/null +++ b/connection_overlay.gd.uid @@ -0,0 +1 @@ +uid://b6m5e50j1syr7 diff --git a/environment_manager.gd b/environment_manager.gd index bcc84aa..c908b39 100644 --- a/environment_manager.gd +++ b/environment_manager.gd @@ -3,7 +3,7 @@ extends Node3D var hand_associations: Dictionary[String, PlayerHand] -@export var web_client: WebClientTCP +@export var web_client: WebClient @export var card_stack: CardStack diff --git a/game.tscn b/game.tscn index 117eb5c..e1fb4f8 100644 --- a/game.tscn +++ b/game.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=13 format=3 uid="uid://gqrdpjslr4np"] +[gd_scene load_steps=14 format=3 uid="uid://gqrdpjslr4np"] -[ext_resource type="Script" uid="uid://p5gvnyhjqe6o" path="res://components/web_client_tcp.gd" id="1_feb5d"] +[ext_resource type="Script" uid="uid://p5gvnyhjqe6o" path="res://components/web_client.gd" id="1_feb5d"] [ext_resource type="Script" uid="uid://c0cqkatyo4uj1" path="res://components/client_interface.gd" id="2_e2o6t"] [ext_resource type="Theme" uid="uid://7bv5whocnq53" path="res://theme.tres" id="2_hve3p"] [ext_resource type="Script" uid="uid://utt8atbh7b2a" path="res://game_client.gd" id="3_feb5d"] @@ -8,6 +8,7 @@ [ext_resource type="PackedScene" uid="uid://jlovgr2iojrg" path="res://elements/player_hand.tscn" id="4_fc0e3"] [ext_resource type="Script" uid="uid://bfhknl0awlj22" path="res://chat_panel.gd" id="4_iotsf"] [ext_resource type="Script" uid="uid://bokp83oigikj2" path="res://number_input.gd" id="4_vef74"] +[ext_resource type="Script" uid="uid://b6m5e50j1syr7" path="res://connection_overlay.gd" id="5_iotsf"] [ext_resource type="PackedScene" uid="uid://b55xpcxk7g5ph" path="res://cards/playing_card.tscn" id="6_eow3j"] [ext_resource type="PackedScene" uid="uid://dj1wye7o27vil" path="res://assets/table.glb" id="7_eow3j"] [ext_resource type="Script" uid="uid://c5bk4gjeyh7vy" path="res://card_stack.gd" id="8_j5wjh"] @@ -19,8 +20,9 @@ ambient_light_energy = 0.15 [node name="Game" type="Node"] -[node name="WebClient" type="Node" parent="."] +[node name="WebClient" type="Node" parent="." node_paths=PackedStringArray("chat_panel")] script = ExtResource("1_feb5d") +chat_panel = NodePath("../Interface/ChatPanel") [node name="Interface" type="Control" parent="."] layout_mode = 3 @@ -32,7 +34,7 @@ grow_vertical = 2 theme = ExtResource("2_hve3p") metadata/_edit_use_anchors_ = true -[node name="ClientInterface" type="ColorRect" parent="Interface" node_paths=PackedStringArray("client", "connect_button", "create_lobby_button", "join_lobby_button", "leave_lobby_button", "chat_message_input", "send_chat_message_button", "connected_players_label", "name_input", "lobby_code_input")] +[node name="ClientInterface" type="ColorRect" parent="Interface" node_paths=PackedStringArray("client", "create_lobby_button", "join_lobby_button", "leave_lobby_button", "connected_players_label", "name_input", "lobby_code_input")] custom_minimum_size = Vector2(316, 0) layout_mode = 2 offset_right = 316.0 @@ -40,12 +42,9 @@ offset_bottom = 429.0 color = Color(0.26, 0.2236, 0.230273, 1) script = ExtResource("2_e2o6t") client = NodePath("../../WebClient") -connect_button = NodePath("MarginContainer/VBoxContainer/ServerInputs/ConnectButton") create_lobby_button = NodePath("MarginContainer/VBoxContainer/ServerInputs/CreateLobbyButton") join_lobby_button = NodePath("MarginContainer/VBoxContainer/ServerInputs/JoinLobbyButton") leave_lobby_button = NodePath("MarginContainer/VBoxContainer/ServerInputs/LeaveLobbyButton") -chat_message_input = NodePath("../ChatPanel/InputContainer/MessageInput") -send_chat_message_button = NodePath("../ChatPanel/InputContainer/SendMessageButton") connected_players_label = NodePath("MarginContainer/VBoxContainer/ConnectedPlayersLabel") name_input = NodePath("MarginContainer/VBoxContainer/ServerInputs/NameInput") lobby_code_input = NodePath("MarginContainer/VBoxContainer/ServerInputs/LobbyCodeInput") @@ -69,15 +68,6 @@ layout_mode = 2 custom_minimum_size = Vector2(300, 0) layout_mode = 2 -[node name="URLInput" type="LineEdit" parent="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs"] -layout_mode = 2 -text = "127.0.0.1:9974" -placeholder_text = "URL" - -[node name="ConnectButton" type="Button" parent="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs"] -layout_mode = 2 -text = "Connect to Server" - [node name="NameInput" type="LineEdit" parent="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs"] layout_mode = 2 placeholder_text = "Your Name" @@ -112,6 +102,8 @@ anchors_preset = -1 anchor_top = 0.478704 anchor_right = 0.234375 anchor_bottom = 1.0 +offset_right = -130.0 +offset_bottom = 0.000244141 grow_vertical = 0 script = ExtResource("4_iotsf") auto_scroll_duration = 0.4 @@ -130,136 +122,9 @@ vertical_scroll_mode = 4 [node name="MessageContainer" type="VBoxContainer" parent="Interface/ChatPanel/ScrollContainer"] layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 3 alignment = 2 -[node name="Label" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label2" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label3" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label4" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label5" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label6" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label7" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label8" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label9" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label10" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label11" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label12" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label13" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label14" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label15" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label16" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label17" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label18" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label19" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label20" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label21" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label22" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label23" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label24" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label25" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label26" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label27" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label28" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label29" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label30" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label31" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - -[node name="Label32" type="Label" parent="Interface/ChatPanel/ScrollContainer/MessageContainer"] -layout_mode = 2 -text = "asdf" - [node name="InputContainer" type="HBoxContainer" parent="Interface/ChatPanel"] layout_mode = 2 @@ -272,6 +137,43 @@ placeholder_text = "Type chat message here" layout_mode = 2 text = "Send" +[node name="LobbyPanel" type="Control" parent="Interface"] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="ConnectionOverlay" type="Control" parent="Interface" node_paths=PackedStringArray("status_label")] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("5_iotsf") +status_label = NodePath("StatusLabel") + +[node name="ColorRect" type="ColorRect" parent="Interface/ConnectionOverlay"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0.47451, 0.180392, 0.603922, 1) + +[node name="StatusLabel" type="Label" parent="Interface/ConnectionOverlay"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/font_size = 28 +text = "Connecting to server" +horizontal_alignment = 1 +vertical_alignment = 1 + [node name="HBoxContainer" type="HBoxContainer" parent="."] anchors_preset = 15 anchor_right = 1.0 @@ -476,6 +378,8 @@ playing_card_scene = ExtResource("6_eow3j") [connection signal="connected_to_server" from="WebClient" to="Interface/ClientInterface" method="_on_web_client_tcp_connected_to_server"] [connection signal="connection_error_occurred" from="WebClient" to="Interface/ClientInterface" method="_on_web_client_tcp_connection_error_occurred"] [connection signal="connection_status_changed" from="WebClient" to="Interface/ClientInterface" method="_on_web_client_tcp_connection_status_changed"] +[connection signal="connection_status_changed" from="WebClient" to="Interface/ChatPanel" method="_on_web_client_connection_status_changed"] +[connection signal="connection_status_changed" from="WebClient" to="Interface/ConnectionOverlay" method="_on_web_client_connection_status_changed"] [connection signal="disconnected_from_lobby" from="WebClient" to="Interface/ClientInterface" method="_on_web_client_tcp_disconnected_from_lobby"] [connection signal="disconnected_from_server" from="WebClient" to="Interface/ClientInterface" method="_on_web_client_tcp_disconnected_from_server"] [connection signal="new_round_started" from="WebClient" to="EnvironmentManager" method="_on_web_client_tcp_new_round_started"] @@ -497,8 +401,6 @@ playing_card_scene = ExtResource("6_eow3j") [connection signal="reset_first_card" from="WebClient" to="EnvironmentManager/PlayerHand0" method="_on_web_client_tcp_reset_first_card"] [connection signal="trump_received" from="WebClient" to="EnvironmentManager" method="_on_web_client_tcp_trump_received"] [connection signal="trump_received" from="WebClient" to="EnvironmentManager/PlayerHand0" method="_on_web_client_tcp_trump_received"] -[connection signal="user_message_received" from="WebClient" to="Interface/ChatPanel" method="_on_web_client_user_message_received"] -[connection signal="pressed" from="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs/ConnectButton" to="Interface/ClientInterface" method="_on_connect_button_pressed"] [connection signal="pressed" from="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs/CreateLobbyButton" to="Interface/ClientInterface" method="_on_create_lobby_button_pressed"] [connection signal="pressed" from="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs/CreateLobbyButton" to="HBoxContainer/GameUI" method="_on_create_lobby_button_pressed"] [connection signal="pressed" from="Interface/ClientInterface/MarginContainer/VBoxContainer/ServerInputs/JoinLobbyButton" to="Interface/ClientInterface" method="_on_join_lobby_button_pressed"] diff --git a/game_client.gd b/game_client.gd index 28ee8c4..224a82f 100644 --- a/game_client.gd +++ b/game_client.gd @@ -1,7 +1,7 @@ class_name GameClient extends Node -@export var web_client: WebClientTCP +@export var web_client: WebClient @export var start_game_button: Button @export var play_card_button: Button @export var bet_container: Control