Merge pull request #781 from Faless/update/ws

This commit is contained in:
Rémi Verschelde
2022-10-14 13:41:01 +02:00
committed by GitHub
24 changed files with 836 additions and 675 deletions

View File

@@ -0,0 +1,85 @@
[gd_scene format=3 uid="uid://cyvrywci15kev"]
[node name="Chat" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Panel" type="Panel" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Listen" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_right = 1152.0
[node name="Connect" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 4.0
offset_right = 1152.0
offset_bottom = 35.0
[node name="Host" type="LineEdit" parent="Panel/VBoxContainer/Connect"]
offset_right = 930.0
offset_bottom = 31.0
size_flags_horizontal = 3
text = "ws://localhost:8000/test/"
placeholder_text = "ws://my.server/path/"
[node name="Connect" type="Button" parent="Panel/VBoxContainer/Connect"]
offset_left = 934.0
offset_right = 1006.0
offset_bottom = 31.0
toggle_mode = true
text = "Connect"
[node name="Port" type="SpinBox" parent="Panel/VBoxContainer/Connect"]
offset_left = 1010.0
offset_right = 1093.0
offset_bottom = 31.0
min_value = 1.0
max_value = 65535.0
value = 8000.0
[node name="Listen" type="Button" parent="Panel/VBoxContainer/Connect"]
offset_left = 1097.0
offset_right = 1152.0
offset_bottom = 31.0
toggle_mode = true
text = "Listen"
[node name="Send" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 39.0
offset_right = 1152.0
offset_bottom = 70.0
[node name="LineEdit" type="LineEdit" parent="Panel/VBoxContainer/Send"]
offset_right = 1101.0
offset_bottom = 31.0
size_flags_horizontal = 3
placeholder_text = "Enter some text to send..."
[node name="Send" type="Button" parent="Panel/VBoxContainer/Send"]
offset_left = 1105.0
offset_right = 1152.0
offset_bottom = 31.0
text = "Send"
[node name="RichTextLabel" type="RichTextLabel" parent="Panel/VBoxContainer"]
offset_top = 74.0
offset_right = 1152.0
offset_bottom = 648.0
size_flags_vertical = 3

View File

@@ -0,0 +1,47 @@
extends Control
@onready var _client : WebSocketClient = $WebSocketClient
@onready var _log_dest = $Panel/VBoxContainer/RichTextLabel
@onready var _line_edit = $Panel/VBoxContainer/Send/LineEdit
@onready var _host = $Panel/VBoxContainer/Connect/Host
func info(msg):
print(msg)
_log_dest.add_text(str(msg) + "\n")
# Client signals
func _on_web_socket_client_connection_closed():
var ws = _client.get_socket()
info("Client just disconnected with code: %s, reson: %s" % [ws.get_close_code(), ws.get_close_reason()])
func _on_web_socket_client_connected_to_server():
info("Client just connected with protocol: %s" % _client.get_socket().get_selected_protocol())
func _on_web_socket_client_message_received(message):
info("%s" % message)
# UI signals.
func _on_send_pressed():
if _line_edit.text == "":
return
info("Sending message: %s" % [_line_edit.text])
_client.send(_line_edit.text)
_line_edit.text = ""
func _on_connect_toggled(pressed):
if not pressed:
_client.close()
return
if _host.text == "":
return
info("Connecting to host: %s." % [_host.text])
var err = _client.connect_to_url(_host.text)
if err != OK:
info("Error connecting to host: %s" % [_host.text])
return

View File

@@ -0,0 +1,59 @@
[gd_scene load_steps=4 format=3 uid="uid://ph5ghsflqegf"]
[ext_resource type="PackedScene" uid="uid://cyvrywci15kev" path="res://chat.tscn" id="1_cfcun"]
[ext_resource type="Script" path="res://websocket/WebSocketClient.gd" id="2_m4g4y"]
[ext_resource type="Script" path="res://client.gd" id="2_opbid"]
[node name="Client" instance=ExtResource("1_cfcun")]
script = ExtResource("2_opbid")
[node name="WebSocketClient" type="Node" parent="." index="0"]
script = ExtResource("2_m4g4y")
supported_protocols = PackedStringArray("demo-chat")
[node name="Panel" parent="." index="1"]
layout_mode = 1
[node name="VBoxContainer" parent="Panel" index="0"]
layout_mode = 1
[node name="Listen" parent="Panel/VBoxContainer" index="0"]
layout_mode = 2
[node name="Connect" parent="Panel/VBoxContainer" index="1"]
layout_mode = 2
[node name="Host" parent="Panel/VBoxContainer/Connect" index="0"]
layout_mode = 2
offset_right = 1076.0
[node name="Connect" parent="Panel/VBoxContainer/Connect" index="1"]
layout_mode = 2
offset_left = 1080.0
offset_right = 1152.0
[node name="Port" parent="Panel/VBoxContainer/Connect" index="2"]
visible = false
layout_mode = 2
[node name="Listen" parent="Panel/VBoxContainer/Connect" index="3"]
visible = false
layout_mode = 2
[node name="Send" parent="Panel/VBoxContainer" index="2"]
layout_mode = 2
[node name="LineEdit" parent="Panel/VBoxContainer/Send" index="0"]
layout_mode = 2
[node name="Send" parent="Panel/VBoxContainer/Send" index="1"]
layout_mode = 2
[node name="RichTextLabel" parent="Panel/VBoxContainer" index="3"]
layout_mode = 2
[connection signal="connected_to_server" from="WebSocketClient" to="." method="_on_web_socket_client_connected_to_server"]
[connection signal="connection_closed" from="WebSocketClient" to="." method="_on_web_socket_client_connection_closed"]
[connection signal="message_received" from="WebSocketClient" to="." method="_on_web_socket_client_message_received"]
[connection signal="toggled" from="Panel/VBoxContainer/Connect/Connect" to="." method="_on_connect_toggled"]
[connection signal="pressed" from="Panel/VBoxContainer/Send/Send" to="." method="_on_send_pressed"]

View File

@@ -1,84 +0,0 @@
extends Node
@onready var _log_dest = get_parent().get_node(^"Panel/VBoxContainer/RichTextLabel")
var _client = WebSocketClient.new()
var _write_mode = WebSocketPeer.WRITE_MODE_BINARY
var _use_multiplayer = true
var last_connected_client = 0
func _init():
_client.connect(&"connection_established", self._client_connected)
_client.connect(&"connection_error", self._client_disconnected)
_client.connect(&"connection_closed", self._client_disconnected)
_client.connect(&"server_close_request", self._client_close_request)
_client.connect(&"data_received", self._client_received)
_client.connect(&"peer_packet", self._client_received)
_client.connect(&"peer_connected", self._peer_connected)
_client.connect(&"connection_succeeded", self._client_connected, ["multiplayer_protocol"])
_client.connect(&"connection_failed", self._client_disconnected)
func _client_close_request(code, reason):
Utils._log(_log_dest, "Close code: %d, reason: %s" % [code, reason])
func _peer_connected(id):
Utils._log(_log_dest, "%s: Client just connected" % id)
last_connected_client = id
func _exit_tree():
_client.disconnect_from_host(1001, "Bye bye!")
func _process(_delta):
if _client.get_connection_status() == WebSocketClient.CONNECTION_DISCONNECTED:
return
_client.poll()
func _client_connected(protocol):
Utils._log(_log_dest, "Client just connected with protocol: %s" % protocol)
_client.get_peer(1).set_write_mode(_write_mode)
func _client_disconnected(clean=true):
Utils._log(_log_dest, "Client just disconnected. Was clean: %s" % clean)
func _client_received(_p_id = 1):
if _use_multiplayer:
var peer_id = _client.get_packet_peer()
var packet = _client.get_packet()
Utils._log(_log_dest, "MPAPI: From %s Data: %s" % [str(peer_id), Utils.decode_data(packet, false)])
else:
var packet = _client.get_peer(1).get_packet()
var is_string = _client.get_peer(1).was_string_packet()
Utils._log(_log_dest, "Received data. BINARY: %s: %s" % [not is_string, Utils.decode_data(packet, is_string)])
func connect_to_url(host, protocols, multiplayer):
_use_multiplayer = multiplayer
if _use_multiplayer:
_write_mode = WebSocketPeer.WRITE_MODE_BINARY
return _client.connect_to_url(host, protocols, multiplayer)
func disconnect_from_host():
_client.disconnect_from_host(1000, "Bye bye!")
func send_data(data, dest):
_client.get_peer(1).set_write_mode(_write_mode)
if _use_multiplayer:
_client.set_target_peer(dest)
_client.put_packet(Utils.encode_data(data, _write_mode))
else:
_client.get_peer(1).put_packet(Utils.encode_data(data, _write_mode))
func set_write_mode(mode):
_write_mode = mode

View File

@@ -1,89 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://client/client_ui.gd" type="Script" id=1]
[ext_resource path="res://client/client.gd" type="Script" id=2]
[node name="Client" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Panel" type="Panel" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="Connect" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_right = 1024.0
offset_bottom = 24.0
[node name="Host" type="LineEdit" parent="Panel/VBoxContainer/Connect"]
offset_right = 956.0
offset_bottom = 24.0
size_flags_horizontal = 3
text = "ws://localhost:8000/test/"
placeholder_text = "ws://my.server/path/"
[node name="Connect" type="Button" parent="Panel/VBoxContainer/Connect"]
offset_left = 960.0
offset_right = 1024.0
offset_bottom = 24.0
toggle_mode = true
text = "Connect"
[node name="Settings" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 28.0
offset_right = 1024.0
offset_bottom = 52.0
[node name="Mode" type="OptionButton" parent="Panel/VBoxContainer/Settings"]
offset_right = 29.0
offset_bottom = 24.0
[node name="Multiplayer" type="CheckBox" parent="Panel/VBoxContainer/Settings"]
offset_left = 33.0
offset_right = 159.0
offset_bottom = 24.0
pressed = true
text = "Multiplayer API"
[node name="Destination" type="OptionButton" parent="Panel/VBoxContainer/Settings"]
offset_left = 163.0
offset_right = 192.0
offset_bottom = 24.0
[node name="Send" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 56.0
offset_right = 1024.0
offset_bottom = 80.0
[node name="LineEdit" type="LineEdit" parent="Panel/VBoxContainer/Send"]
offset_right = 977.0
offset_bottom = 24.0
size_flags_horizontal = 3
placeholder_text = "Enter some text to send..."
[node name="Send" type="Button" parent="Panel/VBoxContainer/Send"]
offset_left = 981.0
offset_right = 1024.0
offset_bottom = 24.0
text = "Send"
[node name="RichTextLabel" type="RichTextLabel" parent="Panel/VBoxContainer"]
offset_top = 84.0
offset_right = 1024.0
offset_bottom = 600.0
size_flags_vertical = 3
[node name="Client" type="Node" parent="."]
script = ExtResource( 2 )
[connection signal="toggled" from="Panel/VBoxContainer/Connect/Connect" to="." method="_on_Connect_toggled"]
[connection signal="item_selected" from="Panel/VBoxContainer/Settings/Mode" to="." method="_on_Mode_item_selected"]
[connection signal="pressed" from="Panel/VBoxContainer/Send/Send" to="." method="_on_Send_pressed"]

View File

@@ -1,62 +0,0 @@
extends Control
@onready var _client = $Client
@onready var _log_dest = $Panel/VBoxContainer/RichTextLabel
@onready var _line_edit = $Panel/VBoxContainer/Send/LineEdit
@onready var _host = $Panel/VBoxContainer/Connect/Host
@onready var _multiplayer = $Panel/VBoxContainer/Settings/Multiplayer
@onready var _write_mode = $Panel/VBoxContainer/Settings/Mode
@onready var _destination = $Panel/VBoxContainer/Settings/Destination
func _ready():
_write_mode.clear()
_write_mode.add_item("BINARY")
_write_mode.set_item_metadata(0, WebSocketPeer.WRITE_MODE_BINARY)
_write_mode.add_item("TEXT")
_write_mode.set_item_metadata(1, WebSocketPeer.WRITE_MODE_TEXT)
_destination.add_item("Broadcast")
_destination.set_item_metadata(0, 0)
_destination.add_item("Last connected")
_destination.set_item_metadata(1, 1)
_destination.add_item("All But last connected")
_destination.set_item_metadata(2, -1)
_destination.select(0)
func _on_Mode_item_selected(_id):
_client.set_write_mode(_write_mode.get_selected_metadata())
func _on_Send_pressed():
if _line_edit.text == "":
return
var dest = _destination.get_selected_metadata()
if dest > 0:
dest = _client.last_connected_client
elif dest < 0:
dest = -_client.last_connected_client
Utils._log(_log_dest, "Sending data %s to %s" % [_line_edit.text, dest])
_client.send_data(_line_edit.text, dest)
_line_edit.text = ""
func _on_Connect_toggled( pressed ):
if pressed:
var multiplayer = _multiplayer.pressed
if multiplayer:
_write_mode.disabled = true
else:
_destination.disabled = true
_multiplayer.disabled = true
if _host.text != "":
Utils._log(_log_dest, "Connecting to host: %s" % [_host.text])
var supported_protocols = PackedStringArray(["my-protocol2", "my-protocol", "binary"])
_client.connect_to_url(_host.text, supported_protocols, multiplayer)
else:
_destination.disabled = false
_write_mode.disabled = false
_multiplayer.disabled = false
_client.disconnect_from_host()

View File

@@ -0,0 +1,56 @@
[gd_scene load_steps=3 format=3 uid="uid://dye16x7udqrxg"]
[ext_resource type="PackedScene" uid="uid://qvg4q16blgx5" path="res://server.tscn" id="1_0srxc"]
[ext_resource type="PackedScene" uid="uid://ph5ghsflqegf" path="res://client.tscn" id="2_percb"]
[node name="Combo" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
[node name="Box" type="HBoxContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Server" parent="Box" instance=ExtResource("1_0srxc")]
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_right = 574.0
offset_bottom = 648.0
[node name="VBoxContainer" type="VBoxContainer" parent="Box"]
offset_left = 578.0
offset_right = 1152.0
offset_bottom = 648.0
size_flags_horizontal = 3
[node name="Client" parent="Box/VBoxContainer" instance=ExtResource("2_percb")]
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_right = 574.0
offset_bottom = 213.0
[node name="Client2" parent="Box/VBoxContainer" instance=ExtResource("2_percb")]
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_top = 217.0
offset_right = 574.0
offset_bottom = 430.0
[node name="Client3" parent="Box/VBoxContainer" instance=ExtResource("2_percb")]
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_top = 434.0
offset_right = 574.0
offset_bottom = 648.0

View File

@@ -1,53 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://server/server.tscn" type="PackedScene" id=1]
[ext_resource path="res://client/client.tscn" type="PackedScene" id=2]
[node name="Combo" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 1
[node name="Box" type="HBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/separation = 20
[node name="ServerControl" parent="Box" instance=ExtResource( 1 )]
anchor_right = 0.0
anchor_bottom = 0.0
offset_right = 502.0
offset_bottom = 600.0
size_flags_horizontal = 3
[node name="VBoxContainer" type="VBoxContainer" parent="Box"]
offset_left = 522.0
offset_right = 1024.0
offset_bottom = 600.0
size_flags_horizontal = 3
[node name="Client" parent="Box/VBoxContainer" instance=ExtResource( 2 )]
anchor_right = 0.0
anchor_bottom = 0.0
offset_right = 502.0
offset_bottom = 197.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Client2" parent="Box/VBoxContainer" instance=ExtResource( 2 )]
anchor_right = 0.0
anchor_bottom = 0.0
offset_top = 201.0
offset_right = 502.0
offset_bottom = 398.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Client3" parent="Box/VBoxContainer" instance=ExtResource( 2 )]
anchor_right = 0.0
anchor_bottom = 0.0
offset_top = 402.0
offset_right = 502.0
offset_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 3

View File

@@ -1,8 +1,9 @@
[remap]
importer="texture"
type="StreamTexture2D"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
type="CompressedTexture2D"
uid="uid://db0a1leye11ap"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
metadata={
"vram_texture": false
}
@@ -10,26 +11,24 @@ metadata={
[deps]
source_file="res://icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"]
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -6,19 +6,32 @@
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=4
config_version=5
_global_script_classes=[{
"base": "Node",
"class": &"WebSocketClient",
"language": &"GDScript",
"path": "res://websocket/WebSocketClient.gd"
}, {
"base": "Node",
"class": &"WebSocketServer",
"language": &"GDScript",
"path": "res://websocket/WebSocketServer.gd"
}]
_global_script_class_icons={
"WebSocketClient": "",
"WebSocketServer": ""
}
[application]
config/name="WebSocket Chat Demo"
config/description="This is a demo of a simple chat implemented using WebSockets, showing both how to host a websocket server from Godot and how to connect to it."
run/main_scene="res://combo/combo.tscn"
run/main_scene="res://combo.tscn"
config/features=PackedStringArray("4.0")
config/icon="res://icon.png"
[autoload]
Utils="*res://utils.gd"
[gdnative]
singletons=[]

View File

@@ -0,0 +1,51 @@
extends Control
@onready var _server : WebSocketServer = $WebSocketServer
@onready var _log_dest = $Panel/VBoxContainer/RichTextLabel
@onready var _line_edit = $Panel/VBoxContainer/Send/LineEdit
@onready var _listen_port = $Panel/VBoxContainer/Connect/Port
func info(msg):
print(msg)
_log_dest.add_text(str(msg) + "\n")
# Server signals
func _on_web_socket_server_client_connected(peer_id):
var peer : WebSocketPeer = _server.peers[peer_id]
info("Remote client connected: %d. Protocol: %s" % [peer_id, peer.get_selected_protocol()])
_server.send(-peer_id, "[%d] connected" % peer_id)
func _on_web_socket_server_client_disconnected(peer_id):
var peer : WebSocketPeer = _server.peers[peer_id]
info("Remote client disconnected: %d. Code: %d, Reason: %s" % [peer_id, peer.get_close_code(), peer.get_close_reason()])
_server.send(-peer_id, "[%d] disconnected" % peer_id)
func _on_web_socket_server_message_received(peer_id, message):
info("Server received data from peer %d: %s" % [peer_id, message])
_server.send(-peer_id, "[%d] Says: %s" % [peer_id, message])
# UI signals.
func _on_send_pressed():
if _line_edit.text == "":
return
info("Sending message: %s" % [_line_edit.text])
_server.send(0, "Server says: %s" % _line_edit.text)
_line_edit.text = ""
func _on_listen_toggled(pressed):
if not pressed:
_server.stop()
info("Server stopped")
return
var port = int(_listen_port.value)
var err = _server.listen(port)
if err != OK:
info("Error listing on port %s" % port)
return
info("Listing on port %s, supported protocols: %s" % [port, _server.supported_protocols])

View File

@@ -0,0 +1,61 @@
[gd_scene load_steps=4 format=3 uid="uid://qvg4q16blgx5"]
[ext_resource type="PackedScene" uid="uid://cyvrywci15kev" path="res://chat.tscn" id="1_i673i"]
[ext_resource type="Script" path="res://server.gd" id="1_urpfw"]
[ext_resource type="Script" path="res://websocket/WebSocketServer.gd" id="3_0eqsy"]
[node name="Server" instance=ExtResource("1_i673i")]
script = ExtResource("1_urpfw")
[node name="WebSocketServer" type="Node" parent="." index="0"]
script = ExtResource("3_0eqsy")
supported_protocols = PackedStringArray("demo-chat")
[node name="Panel" parent="." index="1"]
layout_mode = 3
[node name="VBoxContainer" parent="Panel" index="0"]
layout_mode = 3
[node name="Listen" parent="Panel/VBoxContainer" index="0"]
layout_mode = 3
[node name="Connect" parent="Panel/VBoxContainer" index="1"]
layout_mode = 3
[node name="Host" parent="Panel/VBoxContainer/Connect" index="0"]
visible = false
layout_mode = 3
offset_right = 1006.0
[node name="Connect" parent="Panel/VBoxContainer/Connect" index="1"]
visible = false
layout_mode = 3
[node name="Port" parent="Panel/VBoxContainer/Connect" index="2"]
layout_mode = 3
offset_left = 0.0
offset_right = 83.0
[node name="Listen" parent="Panel/VBoxContainer/Connect" index="3"]
layout_mode = 3
offset_left = 87.0
offset_right = 142.0
[node name="Send" parent="Panel/VBoxContainer" index="2"]
layout_mode = 3
[node name="LineEdit" parent="Panel/VBoxContainer/Send" index="0"]
layout_mode = 3
[node name="Send" parent="Panel/VBoxContainer/Send" index="1"]
layout_mode = 3
[node name="RichTextLabel" parent="Panel/VBoxContainer" index="3"]
layout_mode = 3
[connection signal="client_connected" from="WebSocketServer" to="." method="_on_web_socket_server_client_connected"]
[connection signal="client_disconnected" from="WebSocketServer" to="." method="_on_web_socket_server_client_disconnected"]
[connection signal="message_received" from="WebSocketServer" to="." method="_on_web_socket_server_message_received"]
[connection signal="toggled" from="Panel/VBoxContainer/Connect/Listen" to="." method="_on_listen_toggled"]
[connection signal="pressed" from="Panel/VBoxContainer/Send/Send" to="." method="_on_send_pressed"]

View File

@@ -1,84 +0,0 @@
extends Node
@onready var _log_dest = get_parent().get_node(^"Panel/VBoxContainer/RichTextLabel")
var _server = WebSocketServer.new()
var _clients = {}
var _write_mode = WebSocketPeer.WRITE_MODE_BINARY
var _use_multiplayer = true
var last_connected_client = 0
func _init():
_server.connect(&"client_connected", self._client_connected)
_server.connect(&"client_disconnected", self._client_disconnected)
_server.connect(&"client_close_request", self._client_close_request)
_server.connect(&"data_received", self._client_receive)
_server.connect(&"peer_packet", self._client_receive)
_server.connect(&"peer_connected", self._client_connected, ["multiplayer_protocol"])
_server.connect(&"peer_disconnected", self._client_disconnected)
func _exit_tree():
_clients.clear()
_server.stop()
func _process(_delta):
if _server.is_listening():
_server.poll()
func _client_close_request(id, code, reason):
print(reason == "Bye bye!")
Utils._log(_log_dest, "Client %s close code: %d, reason: %s" % [id, code, reason])
func _client_connected(id, protocol):
_clients[id] = _server.get_peer(id)
_clients[id].set_write_mode(_write_mode)
last_connected_client = id
Utils._log(_log_dest, "%s: Client connected with protocol %s" % [id, protocol])
func _client_disconnected(id, clean = true):
Utils._log(_log_dest, "Client %s disconnected. Was clean: %s" % [id, clean])
if _clients.has(id):
_clients.erase(id)
func _client_receive(id):
if _use_multiplayer:
var peer_id = _server.get_packet_peer()
var packet = _server.get_packet()
Utils._log(_log_dest, "MPAPI: From %s data: %s" % [peer_id, Utils.decode_data(packet, false)])
else:
var packet = _server.get_peer(id).get_packet()
var is_string = _server.get_peer(id).was_string_packet()
Utils._log(_log_dest, "Data from %s BINARY: %s: %s" % [id, not is_string, Utils.decode_data(packet, is_string)])
func send_data(data, dest):
if _use_multiplayer:
_server.set_target_peer(dest)
_server.put_packet(Utils.encode_data(data, _write_mode))
else:
for id in _clients:
_server.get_peer(id).put_packet(Utils.encode_data(data, _write_mode))
func listen(port, supported_protocols, multiplayer):
_use_multiplayer = multiplayer
if _use_multiplayer:
set_write_mode(WebSocketPeer.WRITE_MODE_BINARY)
return _server.listen(port, supported_protocols, multiplayer)
func stop():
_server.stop()
func set_write_mode(mode):
_write_mode = mode
for c in _clients:
_clients[c].set_write_mode(_write_mode)

View File

@@ -1,88 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://server/server_ui.gd" type="Script" id=1]
[ext_resource path="res://server/server.gd" type="Script" id=2]
[node name="ServerControl" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Server" type="Node" parent="."]
script = ExtResource( 2 )
[node name="Panel" type="Panel" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_right = 1024.0
offset_bottom = 24.0
[node name="Port" type="SpinBox" parent="Panel/VBoxContainer/HBoxContainer"]
offset_right = 74.0
offset_bottom = 24.0
min_value = 1.0
max_value = 65535.0
value = 8000.0
[node name="Listen" type="Button" parent="Panel/VBoxContainer/HBoxContainer"]
offset_left = 78.0
offset_right = 129.0
offset_bottom = 24.0
toggle_mode = true
text = "Listen"
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 28.0
offset_right = 1024.0
offset_bottom = 52.0
[node name="WriteMode" type="OptionButton" parent="Panel/VBoxContainer/HBoxContainer2"]
offset_right = 29.0
offset_bottom = 24.0
[node name="MPAPI" type="CheckBox" parent="Panel/VBoxContainer/HBoxContainer2"]
offset_left = 33.0
offset_right = 159.0
offset_bottom = 24.0
pressed = true
text = "Multiplayer API"
[node name="Destination" type="OptionButton" parent="Panel/VBoxContainer/HBoxContainer2"]
offset_left = 163.0
offset_right = 192.0
offset_bottom = 24.0
[node name="HBoxContainer3" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 56.0
offset_right = 1024.0
offset_bottom = 80.0
[node name="LineEdit" type="LineEdit" parent="Panel/VBoxContainer/HBoxContainer3"]
offset_right = 977.0
offset_bottom = 24.0
size_flags_horizontal = 3
[node name="Send" type="Button" parent="Panel/VBoxContainer/HBoxContainer3"]
offset_left = 981.0
offset_right = 1024.0
offset_bottom = 24.0
text = "Send"
[node name="RichTextLabel" type="RichTextLabel" parent="Panel/VBoxContainer"]
offset_top = 84.0
offset_right = 1024.0
offset_bottom = 600.0
size_flags_vertical = 3
[connection signal="toggled" from="Panel/VBoxContainer/HBoxContainer/Listen" to="." method="_on_Listen_toggled"]
[connection signal="item_selected" from="Panel/VBoxContainer/HBoxContainer2/WriteMode" to="." method="_on_WriteMode_item_selected"]
[connection signal="pressed" from="Panel/VBoxContainer/HBoxContainer3/Send" to="." method="_on_Send_pressed"]

View File

@@ -1,70 +0,0 @@
extends Control
@onready var _server = $Server
@onready var _port = $Panel/VBoxContainer/HBoxContainer/Port
@onready var _line_edit = $Panel/VBoxContainer/HBoxContainer3/LineEdit
@onready var _write_mode = $Panel/VBoxContainer/HBoxContainer2/WriteMode
@onready var _log_dest = $Panel/VBoxContainer/RichTextLabel
@onready var _multiplayer = $Panel/VBoxContainer/HBoxContainer2/MPAPI
@onready var _destination = $Panel/VBoxContainer/HBoxContainer2/Destination
func _ready():
_write_mode.clear()
_write_mode.add_item("BINARY")
_write_mode.set_item_metadata(0, WebSocketPeer.WRITE_MODE_BINARY)
_write_mode.add_item("TEXT")
_write_mode.set_item_metadata(1, WebSocketPeer.WRITE_MODE_TEXT)
_write_mode.select(0)
_destination.add_item("Broadcast")
_destination.set_item_metadata(0, 0)
_destination.add_item("Last connected")
_destination.set_item_metadata(1, 1)
_destination.add_item("All But last connected")
_destination.set_item_metadata(2, -1)
_destination.select(0)
func _on_Listen_toggled(pressed):
if pressed:
var use_multiplayer = _multiplayer.pressed
_multiplayer.disabled = true
var supported_protocols = PackedStringArray(["my-protocol", "binary"])
var port = int(_port.value)
if use_multiplayer:
_write_mode.disabled = true
_write_mode.select(0)
else:
_destination.disabled = true
_destination.select(0)
if _server.listen(port, supported_protocols, use_multiplayer) == OK:
Utils._log(_log_dest, "Listing on port %s" % port)
if not use_multiplayer:
Utils._log(_log_dest, "Supported protocols: %s" % supported_protocols)
else:
Utils._log(_log_dest, "Error listening on port %s" % port)
else:
_server.stop()
_multiplayer.disabled = false
_write_mode.disabled = false
_destination.disabled = false
Utils._log(_log_dest, "Server stopped")
func _on_Send_pressed():
if _line_edit.text == "":
return
var dest = _destination.get_selected_metadata()
if dest > 0:
dest = _server.last_connected_client
elif dest < 0:
dest = -_server.last_connected_client
Utils._log(_log_dest, "Sending data %s to %s" % [_line_edit.text, dest])
_server.send_data(_line_edit.text, dest)
_line_edit.text = ""
func _on_WriteMode_item_selected(_id):
_server.set_write_mode(_write_mode.get_selected_metadata())

View File

@@ -1,17 +0,0 @@
extends Node
func encode_data(data, mode):
if mode == WebSocketPeer.WRITE_MODE_TEXT:
return data.to_utf8()
return var2bytes(data)
func decode_data(data, is_string):
if is_string:
return data.get_string_from_utf8()
return bytes2var(data)
func _log(node, msg):
print(msg)
node.add_text(str(msg) + "\n")

View File

@@ -0,0 +1,73 @@
extends Node
class_name WebSocketClient
@export var handshake_headers : PackedStringArray
@export var supported_protocols : PackedStringArray
@export var tls_trusted_certificate : X509Certificate
@export var tls_verify := true
var socket = WebSocketPeer.new()
var last_state = WebSocketPeer.STATE_CLOSED
signal connected_to_server()
signal connection_closed()
signal message_received(message: Variant)
func connect_to_url(url) -> int:
socket.supported_protocols = supported_protocols
socket.handshake_headers = handshake_headers
var err = socket.connect_to_url(url, tls_verify, tls_trusted_certificate)
if err != OK:
return err
last_state = socket.get_ready_state()
return OK
func send(message) -> int:
if typeof(message) == TYPE_STRING:
return socket.send_text(message)
return socket.send(var_to_bytes(message))
func get_message() -> Variant:
if socket.get_available_packet_count() < 1:
return null
var pkt = socket.get_packet()
if socket.was_string_packet():
return pkt.get_string_from_utf8()
return bytes_to_var(pkt)
func close(code := 1000, reason := "") -> void:
socket.close(code, reason)
last_state = socket.get_ready_state()
func clear() -> void:
socket = WebSocketPeer.new()
last_state = socket.get_ready_state()
func get_socket() -> WebSocketPeer:
return socket
func poll() -> void:
if socket.get_ready_state() != socket.STATE_CLOSED:
socket.poll()
var state = socket.get_ready_state()
if last_state != state:
last_state = state
if state == socket.STATE_OPEN:
connected_to_server.emit()
elif state == socket.STATE_CLOSED:
connection_closed.emit()
while socket.get_ready_state() == socket.STATE_OPEN and socket.get_available_packet_count():
message_received.emit(get_message())
func _process(delta):
poll()

View File

@@ -0,0 +1,162 @@
extends Node
class_name WebSocketServer
signal message_received(peer_id : int, message)
signal client_connected(peer_id : int)
signal client_disconnected(peer_id : int)
@export var handshake_headers := PackedStringArray()
@export var supported_protocols : PackedStringArray
@export var handshake_timout := 3000
@export var use_tls := false
@export var tls_cert : X509Certificate
@export var tls_key : CryptoKey
@export var refuse_new_connections := false :
set(refuse):
if refuse:
pending_peers.clear()
class PendingPeer:
var connect_time : int
var tcp : StreamPeerTCP
var connection : StreamPeer
var ws : WebSocketPeer
func _init(p_tcp: StreamPeerTCP):
tcp = p_tcp
connection = p_tcp
connect_time = Time.get_ticks_msec()
var tcp_server := TCPServer.new()
var pending_peers : Array[PendingPeer] = []
var peers : Dictionary
func listen(port : int) -> int:
assert(not tcp_server.is_listening())
return tcp_server.listen(port)
func stop():
tcp_server.stop()
pending_peers.clear()
peers.clear()
func send(peer_id, message) -> int:
var type = typeof(message)
if peer_id <= 0:
# Send to multiple peers, (zero = brodcast, negative = exclude one)
for id in peers:
if id == -peer_id:
continue
if type == TYPE_STRING:
peers[id].send_text(message)
else:
peers[id].put_packet(message)
return OK
assert(peers.has(peer_id))
var socket = peers[peer_id]
if type == TYPE_STRING:
return socket.send_text(message)
return socket.send(var_to_bytes(message))
func get_message(peer_id) -> Variant:
assert(peers.has(peer_id))
var socket = peers[peer_id]
if socket.get_available_packet_count() < 1:
return null
var pkt = socket.get_packet()
if socket.was_string_packet():
return pkt.get_string_from_utf8()
return bytes_to_var(pkt)
func has_message(peer_id) -> bool:
assert(peers.has(peer_id))
return peers[peer_id].get_available_packet_count() > 0
func _create_peer() -> WebSocketPeer:
var ws = WebSocketPeer.new()
ws.supported_protocols = supported_protocols
ws.handshake_headers = handshake_headers
return ws
func poll() -> void:
if not tcp_server.is_listening():
return
while not refuse_new_connections and tcp_server.is_connection_available():
var conn = tcp_server.take_connection()
assert(conn != null)
pending_peers.append(PendingPeer.new(conn))
var to_remove := []
for p in pending_peers:
if not _connect_pending(p):
if p.connect_time + handshake_timout < Time.get_ticks_msec():
# Timeout
to_remove.append(p)
continue # Still pending
to_remove.append(p)
for r in to_remove:
pending_peers.erase(r)
to_remove.clear()
for id in peers:
var p : WebSocketPeer = peers[id]
var packets = p.get_available_packet_count()
p.poll()
if p.get_ready_state() != WebSocketPeer.STATE_OPEN:
client_disconnected.emit(id)
to_remove.append(id)
continue
while p.get_available_packet_count():
message_received.emit(id, get_message(id))
for r in to_remove:
peers.erase(r)
to_remove.clear()
func _connect_pending(p: PendingPeer) -> bool:
if p.ws != null:
# Poll websocket client if doing handshake
p.ws.poll()
var state = p.ws.get_ready_state()
if state == WebSocketPeer.STATE_OPEN:
var id = randi_range(2, 1 << 30)
peers[id] = p.ws
client_connected.emit(id)
return true # Success.
elif state != WebSocketPeer.STATE_CONNECTING:
return true # Failure.
return false # Still connecting.
elif p.tcp.get_status() != StreamPeerTCP.STATUS_CONNECTED:
return true # TCP disconnected.
elif not use_tls:
# TCP is ready, create WS peer
p.ws = _create_peer()
p.ws.accept_stream(p.tcp)
return false # WebSocketPeer connection is pending.
else:
if p.connection == p.tcp:
assert(tls_key != null and tls_cert != null)
var tls = StreamPeerTLS.new()
tls.accept_stream(p.tcp, tls_key, tls_cert)
p.connection = tls
p.connection.poll()
var status = p.connection.get_status()
if status == StreamPeerTLS.STATUS_CONNECTED:
p.ws = _create_peer()
p.ws.accept_stream(p.connection)
return false # WebSocketPeer connection is pending.
if status != StreamPeerTLS.STATUS_HANDSHAKING:
return true # Failure.
return false
func _process(delta):
poll()

View File

@@ -6,13 +6,14 @@
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=4
config_version=5
[application]
config/name="WebSocket Multiplayer Demo"
config/description="This is a sample showing how the use WebSockets along with the Multiplayer API in Godot."
run/main_scene="res://scene/main.tscn"
run/main_scene="res://scene/combo.tscn"
config/features=PackedStringArray("4.0")
config/icon="res://icon.png"
[rendering]

View File

@@ -0,0 +1,66 @@
[gd_scene load_steps=3 format=3 uid="uid://cwmhra3pt1h83"]
[ext_resource type="Script" path="res://script/combo.gd" id="1_8i0ov"]
[ext_resource type="PackedScene" uid="uid://c240icwf4uov8" path="res://scene/main.tscn" id="2_reiiv"]
[node name="Combo" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_8i0ov")
[node name="GridContainer" type="GridContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
columns = 2
[node name="Main" parent="GridContainer" instance=ExtResource("2_reiiv")]
layout_mode = 2
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_right = 574.0
offset_bottom = 322.0
grow_horizontal = 1
grow_vertical = 1
[node name="Main2" parent="GridContainer" instance=ExtResource("2_reiiv")]
layout_mode = 2
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 578.0
offset_right = 1152.0
offset_bottom = 322.0
grow_horizontal = 1
grow_vertical = 1
[node name="Main3" parent="GridContainer" instance=ExtResource("2_reiiv")]
layout_mode = 2
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_top = 326.0
offset_right = 574.0
offset_bottom = 648.0
grow_horizontal = 1
grow_vertical = 1
[node name="Main4" parent="GridContainer" instance=ExtResource("2_reiiv")]
layout_mode = 2
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 578.0
offset_top = 326.0
offset_right = 1152.0
offset_bottom = 648.0
grow_horizontal = 1
grow_vertical = 1

View File

@@ -1,72 +1,83 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=3 format=3 uid="uid://c240icwf4uov8"]
[ext_resource path="res://script/main.gd" type="Script" id=1]
[ext_resource path="res://scene/game.tscn" type="PackedScene" id=2]
[ext_resource type="Script" path="res://script/main.gd" id="1"]
[ext_resource type="PackedScene" path="res://scene/game.tscn" id="2"]
[node name="Main" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1")
[node name="Panel" type="Panel" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 20.0
offset_top = 20.0
offset_right = -20.0
offset_bottom = -20.0
grow_horizontal = 2
grow_vertical = 2
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_right = 984.0
offset_bottom = 24.0
offset_right = 1112.0
offset_bottom = 31.0
[node name="Label" type="Label" parent="Panel/VBoxContainer/HBoxContainer"]
offset_top = 5.0
offset_right = 326.0
offset_bottom = 19.0
offset_top = 2.0
offset_right = 369.0
offset_bottom = 28.0
size_flags_horizontal = 3
text = "Name"
[node name="NameEdit" type="LineEdit" parent="Panel/VBoxContainer/HBoxContainer"]
offset_left = 330.0
offset_right = 984.0
offset_bottom = 24.0
offset_left = 373.0
offset_right = 1112.0
offset_bottom = 31.0
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "A Godot User"
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/VBoxContainer"]
offset_top = 28.0
offset_right = 984.0
offset_bottom = 52.0
offset_top = 35.0
offset_right = 1112.0
offset_bottom = 66.0
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer/HBoxContainer2"]
offset_right = 326.0
offset_bottom = 24.0
offset_right = 369.0
offset_bottom = 31.0
size_flags_horizontal = 3
[node name="Host" type="Button" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
offset_right = 42.0
offset_bottom = 24.0
offset_right = 44.0
offset_bottom = 31.0
text = "Host"
[node name="Control" type="Control" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
offset_left = 46.0
offset_right = 241.0
offset_bottom = 24.0
layout_mode = 3
anchors_preset = 0
offset_left = 48.0
offset_right = 273.0
offset_bottom = 31.0
size_flags_horizontal = 3
[node name="Connect" type="Button" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
offset_left = 245.0
offset_right = 326.0
offset_bottom = 24.0
offset_left = 277.0
offset_right = 369.0
offset_bottom = 31.0
text = "Connect to"
[node name="Disconnect" type="Button" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
@@ -77,36 +88,31 @@ offset_bottom = 24.0
text = "Disconnect"
[node name="Hostname" type="LineEdit" parent="Panel/VBoxContainer/HBoxContainer2"]
offset_left = 330.0
offset_right = 984.0
offset_bottom = 24.0
offset_left = 373.0
offset_right = 1112.0
offset_bottom = 31.0
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "localhost"
placeholder_text = "localhost"
[node name="Control" type="Control" parent="Panel/VBoxContainer"]
offset_top = 56.0
offset_right = 984.0
offset_bottom = 76.0
rect_min_size = Vector2(0, 20)
layout_mode = 3
anchors_preset = 0
offset_top = 70.0
offset_right = 1112.0
offset_bottom = 70.0
[node name="Game" parent="Panel/VBoxContainer" instance=ExtResource( 2 )]
[node name="Game" parent="Panel/VBoxContainer" instance=ExtResource("2")]
layout_mode = 3
anchors_preset = 0
anchor_right = 0.0
anchor_bottom = 0.0
offset_top = 80.0
offset_right = 984.0
offset_bottom = 560.0
offset_top = 74.0
offset_right = 1112.0
offset_bottom = 608.0
[node name="AcceptDialog" type="AcceptDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -200.0
offset_top = -100.0
offset_right = 200.0
offset_bottom = 100.0
dialog_text = "Connection closed"
[connection signal="pressed" from="Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Host" to="." method="_on_Host_pressed"]

View File

@@ -0,0 +1,16 @@
extends Control
var paths := []
func _enter_tree():
for ch in $GridContainer.get_children():
paths.append(NodePath(str(get_path()) + "/GridContainer/" + str(ch.name)))
# Sets a dedicated Multiplayer API for each branch.
for path in paths:
get_tree().set_multiplayer(MultiplayerAPI.create_default_interface(), path)
func _exit_tree():
# Clear the branch-specific Multiplayer API.
for path in paths:
get_tree().set_multiplayer(null, path)

View File

@@ -5,36 +5,50 @@ const _crown = preload("res://img/crown.png")
@onready var _list = $HBoxContainer/VBoxContainer/ItemList
@onready var _action = $HBoxContainer/VBoxContainer/Action
const ACTIONS = ["roll", "pass"]
var _players = []
var _turn = -1
master func set_player_name(name):
var sender = get_tree().get_rpc_sender_id()
rpc("update_player_name", sender, name)
@rpc func _log(what):
$HBoxContainer/RichTextLabel.add_text(what + "\n")
remotesync func update_player_name(player, name):
@rpc(any_peer) func set_player_name(name):
if not is_multiplayer_authority():
return
var sender = multiplayer.get_remote_sender_id()
update_player_name.rpc(sender, name)
@rpc(call_local) func update_player_name(player, name):
var pos = _players.find(player)
if pos != -1:
_list.set_item_text(pos, name)
master func request_action(action):
var sender = get_tree().get_rpc_sender_id()
if _players[_turn] != get_tree().get_rpc_sender_id():
rpc("_log", "Someone is trying to cheat! %s" % str(sender))
@rpc(any_peer) func request_action(action):
if not is_multiplayer_authority():
return
var sender = multiplayer.get_remote_sender_id()
if _players[_turn] != sender:
_log.rpc("Someone is trying to cheat! %s" % str(sender))
return
if action not in ACTIONS:
_log.rpc("Invalid action: %s" % action)
return
do_action(action)
next_turn()
remotesync func do_action(action):
var name = _list.get_item_text(_turn)
func do_action(action):
var player_name = _list.get_item_text(_turn)
var val = randi() % 100
rpc("_log", "%s: %ss %d" % [name, action, val])
_log.rpc("%s: %ss %d" % [player_name, action, val])
remotesync func set_turn(turn):
@rpc(call_local) func set_turn(turn):
_turn = turn
if turn >= _players.size():
return
@@ -43,27 +57,27 @@ remotesync func set_turn(turn):
_list.set_item_icon(i, _crown)
else:
_list.set_item_icon(i, null)
_action.disabled = _players[turn] != get_tree().get_network_unique_id()
_action.disabled = _players[turn] != multiplayer.get_unique_id()
remotesync func del_player(id):
@rpc(call_local) func del_player(id):
var pos = _players.find(id)
if pos == -1:
return
_players.remove(pos)
_players.erase(pos)
_list.remove_item(pos)
if _turn > pos:
_turn -= 1
if get_tree().is_network_server():
rpc("set_turn", _turn)
if multiplayer.is_server():
set_turn.rpc(_turn)
remotesync func add_player(id, name=""):
@rpc(call_local) func add_player(id, pname=""):
_players.append(id)
if name == "":
if pname == "":
_list.add_item("... connecting ...", null, false)
else:
_list.add_item(name, null, false)
_list.add_item(pname, null, false)
func get_player_name(pos):
@@ -77,7 +91,7 @@ func next_turn():
_turn += 1
if _turn >= _players.size():
_turn = 0
rpc("set_turn", _turn)
set_turn.rpc(_turn)
func start():
@@ -92,27 +106,25 @@ func stop():
func on_peer_add(id):
if not get_tree().is_network_server():
if not multiplayer.is_server():
return
for i in range(0, _players.size()):
rpc_id(id, "add_player", _players[i], get_player_name(i))
rpc("add_player", id)
rpc_id(id, "set_turn", _turn)
add_player.rpc_id(id, _players[i], get_player_name(i))
add_player.rpc(id)
set_turn.rpc_id(id, _turn)
func on_peer_del(id):
if not get_tree().is_network_server():
if not multiplayer.is_server():
return
rpc("del_player", id)
remotesync func _log(what):
$HBoxContainer/RichTextLabel.add_text(what + "\n")
del_player.rpc(id)
func _on_Action_pressed():
if get_tree().is_network_server():
if multiplayer.is_server():
if _turn != 0:
return
do_action("roll")
next_turn()
else:
rpc_id(1, "request_action", "roll")
request_action.rpc_id(1, "roll")

View File

@@ -10,14 +10,22 @@ const PROTO_NAME = "ludus"
@onready var _host_edit = $Panel/VBoxContainer/HBoxContainer2/Hostname
@onready var _game = $Panel/VBoxContainer/Game
var peer = null
var peer = WebSocketMultiplayerPeer.new()
func _init():
peer.supported_protocols = ["ludus"]
func _ready():
#warning-ignore-all:return_value_discarded
get_tree().connect(&"network_peer_disconnected", self._peer_disconnected)
get_tree().connect(&"network_peer_connected", self._peer_connected)
$AcceptDialog.get_label().align = Label.ALIGN_CENTER
$AcceptDialog.get_label().valign = Label.VALIGN_CENTER
multiplayer.peer_connected.connect(_peer_connected)
multiplayer.peer_disconnected.connect(_peer_disconnected)
multiplayer.server_disconnected.connect(_close_network)
multiplayer.connection_failed.connect(_close_network)
multiplayer.connected_to_server.connect(_connected)
$AcceptDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
$AcceptDialog.get_label().vertical_alignment = VERTICAL_ALIGNMENT_CENTER
# Set the player name according to the system username. Fallback to the path.
if OS.has_environment("USERNAME"):
_name_edit.text = OS.get_environment("USERNAME")
@@ -45,20 +53,15 @@ func stop_game():
func _close_network():
if get_tree().is_connected("server_disconnected", self, "_close_network"):
get_tree().disconnect(&"server_disconnected", self._close_network)
if get_tree().is_connected("connection_failed", self, "_close_network"):
get_tree().disconnect(&"connection_failed", self._close_network)
if get_tree().is_connected("connected_to_server", self, "_connected"):
get_tree().disconnect(&"connected_to_server", self._connected)
stop_game()
$AcceptDialog.show_modal()
$AcceptDialog.get_close_button().grab_focus()
get_tree().set_network_peer(null)
$AcceptDialog.popup_centered()
$AcceptDialog.get_ok_button().grab_focus()
multiplayer.multiplayer_peer = null
peer.close()
func _connected():
_game.rpc("set_player_name", _name_edit.text)
_game.set_player_name.rpc(_name_edit.text)
func _peer_connected(id):
@@ -66,14 +69,14 @@ func _peer_connected(id):
func _peer_disconnected(id):
print("Disconnected %d" % id)
_game.on_peer_del(id)
func _on_Host_pressed():
peer = WebSocketServer.new()
peer.listen(DEF_PORT, PackedStringArray(["ludus"]), true)
get_tree().connect(&"server_disconnected", self._close_network)
get_tree().set_network_peer(peer)
multiplayer.multiplayer_peer = null
peer.create_server(DEF_PORT)
multiplayer.multiplayer_peer = peer
_game.add_player(1, _name_edit.text)
start_game()
@@ -83,9 +86,7 @@ func _on_Disconnect_pressed():
func _on_Connect_pressed():
peer = WebSocketClient.new()
peer.connect_to_url("ws://" + _host_edit.text + ":" + str(DEF_PORT), PackedStringArray([PROTO_NAME]), true)
get_tree().connect(&"connection_failed", self._close_network)
get_tree().connect(&"connected_to_server", self._connected)
get_tree().set_network_peer(peer)
multiplayer.multiplayer_peer = null
peer.create_client("ws://" + _host_edit.text + ":" + str(DEF_PORT))
multiplayer.multiplayer_peer = peer
start_game()