Files
godot-demo-projects/networking/multiplayer_bomber/gamestate.gd
Hugo Locurcio bac1e69164 Use static typing in all demos (#1063)
This leads to code that is easier to understand and runs
faster thanks to GDScript's typed instructions.

The untyped declaration warning is now enabled on all projects
where type hints were added. All projects currently run without
any untyped declration warnings.

Dodge the Creeps and Squash the Creeps demos intentionally don't
use type hints to match the documentation, where type hints haven't
been adopted yet (given its beginner focus).
2024-06-01 12:12:18 +02:00

157 lines
4.4 KiB
GDScript

extends Node
# Default game server port. Can be any number between 1024 and 49151.
# Not on the list of registered or common ports as of May 2024:
# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
const DEFAULT_PORT = 10567
## The maximum number of players.
const MAX_PEERS = 12
var peer: ENetMultiplayerPeer
## Our local player's name.
var player_name := "The Warrior"
# Names for remote players in id:name format.
var players := {}
var players_ready: Array[int] = []
# Signals to let lobby GUI know what's going on.
signal player_list_changed()
signal connection_failed()
signal connection_succeeded()
signal game_ended()
signal game_error(what: int)
# Callback from SceneTree.
func _player_connected(id: int) -> void:
# Registration of a client beings here, tell the connected player that we are here.
register_player.rpc_id(id, player_name)
# Callback from SceneTree.
func _player_disconnected(id: int) -> void:
if has_node("/root/World"):
# Game is in progress.
if multiplayer.is_server():
game_error.emit("Player " + players[id] + " disconnected")
end_game()
else:
# Game is not in progress.
# Unregister this player.
unregister_player(id)
# Callback from SceneTree, only for clients (not server).
func _connected_ok() -> void:
# We just connected to a server
connection_succeeded.emit()
# Callback from SceneTree, only for clients (not server).
func _server_disconnected() -> void:
game_error.emit("Server disconnected")
end_game()
# Callback from SceneTree, only for clients (not server).
func _connected_fail() -> void:
multiplayer.set_network_peer(null) # Remove peer
connection_failed.emit()
# Lobby management functions.
@rpc("any_peer")
func register_player(new_player_name: String) -> void:
var id := multiplayer.get_remote_sender_id()
players[id] = new_player_name
player_list_changed.emit()
func unregister_player(id: int) -> void:
players.erase(id)
player_list_changed.emit()
@rpc("call_local")
func load_world() -> void:
# Change scene.
var world: Node2D = load("res://world.tscn").instantiate()
get_tree().get_root().add_child(world)
get_tree().get_root().get_node("Lobby").hide()
# Set up score.
world.get_node("Score").add_player(multiplayer.get_unique_id(), player_name)
for pn: int in players:
world.get_node("Score").add_player(pn, players[pn])
# Unpause and unleash the game!
get_tree().paused = false
func host_game(new_player_name: String) -> void:
player_name = new_player_name
peer = ENetMultiplayerPeer.new()
peer.create_server(DEFAULT_PORT, MAX_PEERS)
multiplayer.set_multiplayer_peer(peer)
func join_game(ip: String, new_player_name: String) -> void:
player_name = new_player_name
peer = ENetMultiplayerPeer.new()
peer.create_client(ip, DEFAULT_PORT)
multiplayer.set_multiplayer_peer(peer)
func get_player_list() -> Array:
return players.values()
func begin_game() -> void:
assert(multiplayer.is_server())
load_world.rpc()
var world: Node2D = get_tree().get_root().get_node("World")
var player_scene: PackedScene = load("res://player.tscn")
# Create a dictionary with peer ID. and respective spawn points.
# TODO: This could be improved by randomizing spawn points for players.
var spawn_points := {}
spawn_points[1] = 0 # Server in spawn point 0.
var spawn_point_idx := 1
for p: int in players:
spawn_points[p] = spawn_point_idx
spawn_point_idx += 1
for p_id: int in spawn_points:
var spawn_pos: Vector2 = world.get_node("SpawnPoints/" + str(spawn_points[p_id])).position
var player := player_scene.instantiate()
player.synced_position = spawn_pos
player.name = str(p_id)
world.get_node("Players").add_child(player)
# The RPC must be called after the player is added to the scene tree.
player.set_player_name.rpc(player_name if p_id == multiplayer.get_unique_id() else players[p_id])
func end_game() -> void:
if has_node("/root/World"):
# If the game is in progress, end it.
get_node("/root/World").queue_free()
game_ended.emit()
players.clear()
func _ready() -> void:
multiplayer.peer_connected.connect(_player_connected)
multiplayer.peer_disconnected.connect(_player_disconnected)
multiplayer.connected_to_server.connect(_connected_ok)
multiplayer.connection_failed.connect(_connected_fail)
multiplayer.server_disconnected.connect(_server_disconnected)
## Returns an unique-looking player color based on the name's hash.
func get_player_color(p_name: String) -> Color:
return Color.from_hsv(wrapf(p_name.hash() * 0.001, 0.0, 1.0), 0.6, 1.0)