class_name Network extends Node const DEFAULT_PORT := 42005 signal status_changed(new_status: Status, old_status: Status) enum Status { SINGLEPLAYER, CLIENT_CONNECTING, CLIENT_CONNECTED, SERVER_RUNNING } var status := Status.SINGLEPLAYER ## Connects to the server at the given address, which can be a IPv4 address or ## hostname, or an IPv6 address wrapped in square brackets, optionally followed ## by a colon and valid port number (defaults to `42005`). ## ## Returns `OK` if successful or `ERR_CANT_CREATE` if the client could ## not be created, such as if an invalid address is given. func connect_to_server(address_and_port: String) -> int: # Error assert(status == Status.SINGLEPLAYER) # For IPv6 addresses, check if the address contains a square bracket, # then search for the colon that separates address and port from there. var bracket_index := address_and_port.find("]") var colon_index := address_and_port.find(":", maxi(0, bracket_index)) var address := address_and_port var port := DEFAULT_PORT # If a colon is found, split the string into separate address and port. if colon_index >= 0: address = address_and_port.substr(0, colon_index) port = int(address_and_port.substr(colon_index + 1)) # If address is surrounded in square brackets (such as IPv6), strip them. if (address.substr(0, 1) == "[") and (address.substr(address.length() - 1) == "]"): address = address.substr(1, address.length() - 2) var peer := ENetMultiplayerPeer.new() var error := peer.create_client(address, port) if error: return error multiplayer.multiplayer_peer = peer _set_status(Status.CLIENT_CONNECTING) return OK func disconnect_from_server() -> void: assert((status == Status.CLIENT_CONNECTING) or (status == Status.CLIENT_CONNECTED)) _on_server_disconnected() _set_status(Status.SINGLEPLAYER) func start_server(port := DEFAULT_PORT) -> int: # Error assert(status == Status.SINGLEPLAYER) var peer := ENetMultiplayerPeer.new() var error := peer.create_server(port) if error: return error multiplayer.multiplayer_peer = peer _set_status(Status.SERVER_RUNNING) return OK func stop_server() -> void: assert(status == Status.SERVER_RUNNING) _on_server_stopped() _set_status(Status.SINGLEPLAYER) func _ready() -> void: multiplayer.connected_to_server.connect(_on_connected_to_server) multiplayer.server_disconnected.connect(_on_server_disconnected) multiplayer.peer_connected .connect(_on_peer_connected) multiplayer.peer_disconnected .connect(_on_peer_disconnected) multiplayer.connected_to_server.connect(_set_status.bind(Status.CLIENT_CONNECTED)) multiplayer.server_disconnected.connect(_set_status.bind(Status.SINGLEPLAYER)) multiplayer.connection_failed .connect(_set_status.bind(Status.SINGLEPLAYER)) func _on_connected_to_server() -> void: # Set our own peer id to the one the server assigned to us. (Also renames the player node.) Game.LOCAL_PLAYER.network.peer_id = multiplayer.get_unique_id() Game.INSTANCE.change_world(World.new()) func _on_server_disconnected() -> void: # If not properly connected (`_on_connected_to_server` wasn't called), return. if status != Status.CLIENT_CONNECTED: return # Reset the peer id of the player since we're back to singleplayer, where `1` is the server. Game.LOCAL_PLAYER.network.peer_id = 1 Game.INSTANCE.change_world(World.new()) _clear_other_players() func _on_server_stopped() -> void: _clear_other_players() func _on_peer_connected(id: int) -> void: var player: Player = preload("res://player/player.tscn").instantiate() player.get_node("Network").peer_id = id Game.PLAYERS.add_child(player) func _on_peer_disconnected(id: int) -> void: var player: Player = Game.PLAYERS.get_node(str(id)) Game.PLAYERS.remove_child(player) player.queue_free() func _clear_other_players() -> void: for player in Game.PLAYERS.get_children(): if player != Game.LOCAL_PLAYER: player.queue_free() func _set_status(new_status: Status) -> void: if new_status == status: return if new_status == Status.SINGLEPLAYER: # Reset the peer to work as a singleplayer server again. multiplayer.multiplayer_peer.close() multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new() var old_status := status status = new_status status_changed.emit(new_status, old_status)