Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket Minimal: ported to Godot 4.1 #990

Merged
merged 1 commit into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions networking/websocket_minimal/Main.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,74 @@
[node name="Main" type="Node"]

[node name="Server" type="Node" parent="."]
script = ExtResource( "1" )
script = ExtResource("1")

[node name="Client" type="Node" parent="."]
script = ExtResource( "2" )
script = ExtResource("2")

[node name="Control" type="Control" parent="."]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

[node name="HBoxContainer" type="HBoxContainer" parent="Control"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

[node name="ServerContainer" type="VBoxContainer" parent="Control/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3

[node name="LabelServer" type="Label" parent="Control/HBoxContainer/ServerContainer"]
layout_mode = 2
size_flags_horizontal = 4
theme_override_colors/font_color = Color(0.666667, 0.666667, 0.666667, 1)
theme_override_font_sizes/font_size = 30
text = "Server"

[node name="ButtonPong" type="Button" parent="Control/HBoxContainer/ServerContainer"]
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 30
text = "Send Pong"

[node name="TextServer" type="RichTextLabel" parent="Control/HBoxContainer/ServerContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
bbcode_enabled = true
scroll_following = true

[node name="ClientContainer" type="VBoxContainer" parent="Control/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3

[node name="LabelClient" type="Label" parent="Control/HBoxContainer/ClientContainer"]
layout_mode = 2
size_flags_horizontal = 4
theme_override_colors/font_color = Color(0.666667, 0.666667, 0.666667, 1)
theme_override_font_sizes/font_size = 30
text = "Client"

[node name="ButtonPing" type="Button" parent="Control/HBoxContainer/ClientContainer"]
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 30
text = "Send Ping"

[node name="TextClient" type="RichTextLabel" parent="Control/HBoxContainer/ClientContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
bbcode_enabled = true
scroll_following = true

[connection signal="pressed" from="Control/HBoxContainer/ServerContainer/ButtonPong" to="Server" method="_on_button_pong_pressed"]
[connection signal="pressed" from="Control/HBoxContainer/ClientContainer/ButtonPing" to="Client" method="_on_button_ping_pressed"]
2 changes: 1 addition & 1 deletion networking/websocket_minimal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ This is a minimal sample of connecting two peers to each other using websockets.

Language: GDScript

Renderer: GLES 2
Renderer: Forward+

Check out this demo on the asset library: https://godotengine.org/asset-library/asset/539
60 changes: 18 additions & 42 deletions networking/websocket_minimal/client.gd
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
extends Node

# The URL we will connect to.
@export var websocket_url = "ws://localhost:9080"
var websocket_url = "ws://localhost:9080"
var socket := WebSocketPeer.new()

# Our WebSocketClient instance.
var _client = WebSocketClient.new()

func _ready():
# Connect base signals to get notified of connection open, close, and errors.
_client.connection_closed.connect(self._closed)
_client.connection_error.connect(self._closed)
_client.connection_established.connect(self._connected)
# This signal is emitted when not using the Multiplayer API every time
# a full packet is received.
# Alternatively, you could check get_peer(1).get_available_packets() in a loop.
_client.data_received.connect(self._on_data)

# Initiate connection to the given URL.
var err = _client.connect_to_url(websocket_url)
if err != OK:
push_error("Unable to connect.")
set_process(false)

func log_message(message):
var time = "[color=#aaaaaa] %s [/color]" % Time.get_time_string_from_system()
%TextClient.text += time + message + "\n"

func _closed(was_clean = false):
# was_clean will tell you if the disconnection was correctly notified
# by the remote peer before closing the socket.
print("Closed, clean: ", was_clean)
set_process(false)

func _ready():
if socket.connect_to_url(websocket_url) != OK:
log_message("Unable to connect.")
set_process(false)

func _connected(proto = ""):
# This is called on connection, "proto" will be the selected WebSocket
# sub-protocol (which is optional)
print("Connected with protocol: ", proto)
# You MUST always use get_peer(1).put_packet to send data to server,
# and not put_packet directly when not using the MultiplayerAPI.
_client.get_peer(1).put_packet("Test packet".to_utf8_buffer())

func _process(_delta):
socket.poll()

func _on_data():
# Print the received packet, you MUST always use get_peer(1).get_packet
# to receive data from server, and not get_packet directly when not
# using the MultiplayerAPI.
print("Got data from server: ", _client.get_peer(1).get_packet().get_string_from_utf8())
if socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
while socket.get_available_packet_count():
log_message(socket.get_packet().get_string_from_ascii())


func _process(_delta):
# Call this in _process or _physics_process. Data transfer, and signals
# emission will only happen when calling this function.
_client.poll()
func _exit_tree():
socket.close()


func _exit_tree():
_client.disconnect_from_host()
func _on_button_ping_pressed():
socket.send_text("Ping")
9 changes: 7 additions & 2 deletions networking/websocket_minimal/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ config_version=5

config/name="WebSocket Minimal Demo"
config/description="This is a minimal sample of connecting two peers to each other using websockets."
run/main_scene="res://Main.tscn"
config/features=PackedStringArray("4.0")
config/tags=PackedStringArray("demo", "network", "official")
run/main_scene="res://Main.tscn"
config/features=PackedStringArray("4.1")

[display]

window/size/viewport_width=600
window/size/viewport_height=300

[rendering]

Expand Down
67 changes: 23 additions & 44 deletions networking/websocket_minimal/server.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,38 @@ extends Node

# The port we will listen to.
const PORT = 9080
# Our WebSocketServer instance.
var _server = WebSocketServer.new()

func _ready():
# Connect base signals to get notified of new client connections,
# disconnections, and disconnect requests.
_server.client_connected.connect(self._connected)
_server.client_disconnected.connect(self._disconnected)
_server.client_close_request.connect(self._close_request)
# This signal is emitted when not using the Multiplayer API every time a
# full packet is received.
# Alternatively, you could check get_peer(PEER_ID).get_available_packets()
# in a loop for each connected peer.
_server.data_received.connect(self._on_data)
# Start listening on the given port.
var err = _server.listen(PORT)
if err != OK:
push_error("Unable to start server.")
set_process(false)
var tcp_server := TCPServer.new()
var socket := WebSocketPeer.new()


func _connected(id, proto, rname):
# This is called when a new peer connects, "id" will be the assigned peer id,
# "proto" will be the selected WebSocket sub-protocol (which is optional)
print_rich("Client [b]%d[/b] [color=green]connected[/color] with protocol [b]%s[/b] and resource name [b]%s[/b]" % [id, proto, rname])
func log_message(message):
var time = "[color=#aaaaaa] %s [/color]" % Time.get_time_string_from_system()
%TextServer.text += time + message + "\n"


func _close_request(id, code, reason):
# This is called when a client notifies that it wishes to close the connection,
# providing a reason string and close code.
print_rich("Client [b]%d[/b] [color=yellow]disconnecting[/color] with code: [b]%d[/b], reason: [b]%s[/b]" % [id, code, reason])
func _ready():
if tcp_server.listen(PORT) != OK:
log_message("Unable to start server.")
set_process(false)


func _disconnected(id, was_clean = false):
# This is called when a client disconnects, "id" will be the one of the
# disconnecting client, "was_clean" will tell you if the disconnection
# was correctly notified by the remote peer before closing the socket.
print_rich("Client [b]%d[/b] [color=red]disconnected[/color], clean: [b]%s[/b]" % [id, str(was_clean)])
func _process(_delta):
while tcp_server.is_connection_available():
var conn: StreamPeerTCP = tcp_server.take_connection()
assert(conn != null)
socket.accept_stream(conn)

socket.poll()

func _on_data(id):
# Print the received packet, you MUST always use get_peer(id).get_packet to receive data,
# and not get_packet directly when not using the MultiplayerAPI.
var pkt = _server.get_peer(id).get_packet()
print("Got data from client [b]%d[/b]: [b]%s[/b] ... echoing" % [id, pkt.get_string_from_utf8()])
_server.get_peer(id).put_packet(pkt)
if socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
while socket.get_available_packet_count():
log_message(socket.get_packet().get_string_from_ascii())


func _process(_delta):
# Call this in _process or _physics_process.
# Data transfer, and signals emission will only happen when calling this function.
_server.poll()
func _exit_tree():
socket.close()
tcp_server.stop()


func _exit_tree():
_server.stop()
func _on_button_pong_pressed():
socket.send_text("Pong")