multiplayer_and_some_card_art #2

Merged
kensi.chandrawan merged 7 commits from multiplayer-and-card-art into main 2025-01-25 19:58:37 +07:00
3 changed files with 273 additions and 26 deletions
Showing only changes of commit 3521732cd2 - Show all commits

159
main.gd
View file

@ -1,6 +1,7 @@
extends Node extends Node
const PORT = 25565 const PORT = 25565
const PORT_RANGE := 10
const MAX_CONNECTIONS = 20 const MAX_CONNECTIONS = 20
var counter = 0 var counter = 0
@ -8,31 +9,128 @@ var counter = 0
@onready var before_connect_container = get_node("BeforeConnect") as Container @onready var before_connect_container = get_node("BeforeConnect") as Container
@onready var host_text_edit = get_node("%HostTextEdit") as TextEdit @onready var host_text_edit = get_node("%HostTextEdit") as TextEdit
@onready var connect_button = get_node("%ConnectButton") as Button @onready var connect_button = get_node("%ConnectButton") as Button
@onready var host_button = get_node("%HostButton") as Button
@onready var server_discovery = get_node("%ServerDiscovery") as ServerDiscovery
@onready var after_connect_container = get_node("%AfterConnect") as Container @onready var after_connect_container = get_node("%AfterConnect") as Container
@onready var counter_label = get_node("%CounterLabel") as Label @onready var counter_label = get_node("%CounterLabel") as Label
@onready var decrement_button = get_node("%DecrementButton") as Button @onready var decrement_button = get_node("%DecrementButton") as Button
@onready var increment_button = get_node("%IncrementButton") as Button @onready var increment_button = get_node("%IncrementButton") as Button
@onready var disconnect_button = get_node("%DisconnectButton") as Button
@onready var start_game_button = get_node("%StartGameButton") as Button
var active_port: int
var server_buttons: Dictionary = Dictionary()
func _clean_room():
# this is if someone left the room and we will just clean the game room again
# should only called on host
_set_counter(0)
pass
func _on_start_game_clicked():
if !multiplayer.multiplayer_peer:
return
if !multiplayer.get_unique_id() != 1:
return
if (multiplayer.get_peers().size() == 0):
return
# do start game here, should only run on host
pass
func _ready() -> void: func _ready() -> void:
if OS.has_feature("dedicated_server"): host_button.button_up.connect(_on_host_pressed)
print("Starting dedicated server") host_button.text = "Host (%s)" % server_discovery.id
var peer = ENetMultiplayerPeer.new()
var err = peer.create_server(PORT, MAX_CONNECTIONS) start_game_button.button_up.connect(_on_start_game_clicked)
if err:
print(err) print("Attach client")
multiplayer.multiplayer_peer = peer
multiplayer.peer_connected.connect(_on_player_connected) multiplayer.peer_connected.connect(_on_player_connected)
else: multiplayer.peer_disconnected.connect(_on_player_disconnected)
print("Game starting")
#multiplayer.peer_disconnected.connect(_on_player_disconnected)
multiplayer.connected_to_server.connect(_on_connected_ok) multiplayer.connected_to_server.connect(_on_connected_ok)
#multiplayer.connection_failed.connect(_on_connected_fail) #multiplayer.connection_failed.connect(_on_connected_fail)
#multiplayer.server_disconnected.connect(_on_server_disconnected) multiplayer.server_disconnected.connect(_on_server_disconnected)
increment_button.button_up.connect(func(): _increment.rpc()) increment_button.button_up.connect(func(): _increment.rpc())
decrement_button.button_up.connect(func(): _decrement.rpc()) decrement_button.button_up.connect(func(): _decrement.rpc())
connect_button.button_up.connect(_on_connect_pressed) disconnect_button.button_up.connect(_on_disconnect_clicked)
connect_button.get_parent().remove_child(connect_button)
server_discovery.server_added.connect(_on_server_added)
server_discovery.server_removed.connect(_on_server_removed)
func _on_disconnect_clicked():
if (multiplayer.is_server()):
server_discovery.disable_server()
multiplayer.peer_connected.disconnect(_on_player_connected)
for peer_id in multiplayer.get_peers():
multiplayer.disconnect_peer(peer_id)
multiplayer.multiplayer_peer = null
$BeforeConnect.visible = true
$AfterConnect.visible = false
func _on_server_added(id, ip, port):
print("%s | Server added %s %s" % [server_discovery.id, id, ip])
var button = connect_button.duplicate()
before_connect_container.add_child(button)
var key = "%s|%s" % [id,ip]
server_buttons[key] = button
button.text = "%s\nConnect\n%s:%d" % [id, ip, port]
for conn in button.button_down.get_connections():
button.button_down.disconnect(conn)
var _on_button_downed = func():
_do_connect(ip, port)
button.button_down.connect(_on_button_downed)
func _on_server_removed(id, ip):
print("%s | Server removed %s %s" % [server_discovery.id, id, ip])
var key = "%s|%s" % [id,ip]
var button = server_buttons[key]
button.queue_free()
server_buttons.erase(key)
func _do_connect(ip: String, port: int):
var peer = ENetMultiplayerPeer.new()
print("%s Connecting to %s:%d" % [server_discovery.id, ip, port])
peer.create_client(ip, port)
multiplayer.multiplayer_peer = peer
func _on_host_pressed():
print("%s | Starting dedicated server" % server_discovery.id)
var peer = ENetMultiplayerPeer.new()
for offset in range(PORT_RANGE):
if not is_port_available(PORT + offset):
continue
active_port = PORT + offset
var err = peer.create_server(active_port, MAX_CONNECTIONS)
if err:
print(err)
break
multiplayer.multiplayer_peer = peer
_on_connected_ok()
server_discovery.enable_server(active_port)
func is_port_available(port: int) -> bool:
var udp_peer = PacketPeerUDP.new()
var error = udp_peer.bind(port)
if error == OK:
udp_peer.close()
return true
else:
return false
@rpc("any_peer", "call_local") @rpc("any_peer", "call_local")
func _increment(): func _increment():
@ -49,28 +147,45 @@ func _set_counter(value: int):
counter = value counter = value
counter_label.text = str(counter) counter_label.text = str(counter)
func _on_connect_pressed():
var peer = ENetMultiplayerPeer.new()
var parsed: PackedStringArray = host_text_edit.text.split(":")
print("Connecting to ", parsed[0])
peer.create_client(parsed[0] if not parsed[0].is_empty() else "localhost", int(parsed[1]) if parsed.size() > 1 else PORT)
multiplayer.multiplayer_peer = peer
func _on_player_connected(id: int): func _on_player_connected(id: int):
if multiplayer.get_unique_id() == 1: if multiplayer.get_unique_id() == 1:
_set_counter.rpc_id(id, counter) _set_counter.rpc_id(id, counter)
func _on_player_disconnected(): if (multiplayer.get_peers().size() > 0):
pass start_game_button.text = "Start Game"
server_discovery.disable_server()
func _on_player_disconnected(id):
print("%s | _on_player_disconnected %s" % [server_discovery.id, id])
if multiplayer.get_unique_id() == id:
$BeforeConnect.visible = true
$AfterConnect.visible = false
if (id != 1 and multiplayer.get_unique_id() == 1):
_clean_room()
start_game_button.text = "Start Game (Not enough player)"
server_discovery.enable_server(active_port)
func _on_connected_ok(): func _on_connected_ok():
$AfterConnect.visible = true $AfterConnect.visible = true
$BeforeConnect.visible = false $BeforeConnect.visible = false
counter_label.text = str(counter) counter_label.text = str(counter)
if (multiplayer.is_server()):
disconnect_button.text = "Disconnect (Host)"
if start_game_button.get_parent() != after_connect_container:
after_connect_container.add_child(start_game_button)
else:
disconnect_button.text = "Disconnect"
if start_game_button.get_parent() == after_connect_container:
after_connect_container.remove_child(start_game_button)
func _on_connected_fail(): func _on_connected_fail():
pass pass
func _on_server_disconnected(): func _on_server_disconnected():
pass print("%s | _on_server_disconnected" % [server_discovery.id])
$AfterConnect.visible = false
$BeforeConnect.visible = true

View file

@ -1,6 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://c7gn46af6whf8"] [gd_scene load_steps=3 format=3 uid="uid://c7gn46af6whf8"]
[ext_resource type="Script" path="res://main.gd" id="1_e0ud3"] [ext_resource type="Script" path="res://main.gd" id="1_e0ud3"]
[ext_resource type="Script" path="res://server_discovery.gd" id="2_hed18"]
[node name="Main" type="Control"] [node name="Main" type="Control"]
layout_mode = 3 layout_mode = 3
@ -33,12 +34,31 @@ layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
placeholder_text = "192.168.*.*" placeholder_text = "192.168.*.*"
[node name="RefreshButton" type="Button" parent="BeforeConnect"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 64)
layout_mode = 2
text = "Refresh"
[node name="HostButton" type="Button" parent="BeforeConnect"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 64)
layout_mode = 2
text = "Host"
[node name="ConnectButton" type="Button" parent="BeforeConnect"] [node name="ConnectButton" type="Button" parent="BeforeConnect"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(0, 64) custom_minimum_size = Vector2(0, 64)
layout_mode = 2 layout_mode = 2
text = "Connect" text = "Connect"
[node name="DiscoveryTimer" type="Timer" parent="BeforeConnect"]
unique_name_in_owner = true
[node name="ServerDiscovery" type="Node" parent="BeforeConnect"]
unique_name_in_owner = true
script = ExtResource("2_hed18")
[node name="AfterConnect" type="VBoxContainer" parent="."] [node name="AfterConnect" type="VBoxContainer" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
@ -64,6 +84,12 @@ text = "1"
horizontal_alignment = 1 horizontal_alignment = 1
vertical_alignment = 1 vertical_alignment = 1
[node name="DisconnectButton" type="Button" parent="AfterConnect"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Disconnect"
[node name="Actions" type="HBoxContainer" parent="AfterConnect"] [node name="Actions" type="HBoxContainer" parent="AfterConnect"]
layout_mode = 2 layout_mode = 2
@ -78,3 +104,9 @@ unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "+" text = "+"
[node name="StartGameButton" type="Button" parent="AfterConnect"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Start Game"

100
server_discovery.gd Normal file
View file

@ -0,0 +1,100 @@
extends Node
class_name ServerDiscovery
const PORT := 26566
const PORT_RANGE := 10 # cannot listen to the same port on one device
signal server_added(id: String, ip: String, port: int)
signal server_removed(id: String, ip: String)
var server: UDPServer = null
var client: PacketPeerUDP = null
var port_offset: int
var id: String
var servers: Array[String] = []
var enabled := false
func enable_server(server_port: int):
enabled = true
var data = "%s|AVAILABLE|%d" %[id,server_port]
_broadcast_packet(data.to_utf8_buffer())
func disable_server():
enabled = false
var data = "%s|NOT_AVAILABLE" %id
_broadcast_packet(data.to_utf8_buffer())
func _ready():
id = _generate_id()
_init_server()
_init_client()
func _process(_delta):
server.poll()
while(server.is_connection_available()):
var peer = server.take_connection()
while(peer.get_available_packet_count() > 0):
var packet = peer.get_packet()
var packet_str = packet.get_string_from_utf8()
if (packet_str.begins_with(id)):
continue
var ip = peer.get_packet_ip()
var port = peer.get_packet_port()
print(id, " | Received data from ", ip, ":", port, " => ", packet_str)
var data = packet_str.split("|")
var conn_id = data[0]
var conn_detail = data[1]
var key = "%s:%s" %[ip, port]
if (conn_detail == "AVAILABLE" and key not in servers):
var conn_actual_server_port = int(data[2])
servers.append(key)
server_added.emit(conn_id, ip, conn_actual_server_port)
elif (conn_detail == "NOT_AVAILABLE" and key in servers):
servers.erase(key)
server_removed.emit(conn_id, ip)
elif (conn_detail == "DISCOVERY" and enabled):
var available_data = "%s|AVAILABLE" %id
_broadcast_packet(available_data.to_utf8_buffer())
func _exit_tree():
var data = "%s|NOT_AVAILABLE" %id
_broadcast_packet(data.to_utf8_buffer())
func _init_server():
server = UDPServer.new()
for offset in range(PORT_RANGE):
var err = server.listen(PORT + offset)
if !err:
port_offset = offset
break
print("Server ", id, " initialized on port ", PORT + port_offset)
func _init_client():
client = PacketPeerUDP.new()
client.set_broadcast_enabled(true)
var data = "%s|DISCOVERY" %id
_broadcast_packet(data.to_utf8_buffer())
func _broadcast_packet(buffer: PackedByteArray):
for offset in range(PORT_RANGE):
client.connect_to_host("255.255.255.255", PORT + offset)
client.put_packet(buffer)
func _generate_id() -> String:
var id_len = 5
var from = 'A'.unicode_at(0)
var to = 'Z'.unicode_at(0)
var result:= ""
for i in range(id_len):
var rand_char = char(randi_range(from, to))
result += rand_char
return result