mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2026-01-06 07:50:22 +01:00
Add WebSocket high level multiplayer demo.
This commit is contained in:
3
networking/websocket_multiplayer/.gitignore
vendored
Normal file
3
networking/websocket_multiplayer/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.import/*
|
||||
*.import
|
||||
export_presets.cfg
|
||||
20
networking/websocket_multiplayer/default_env.tres
Normal file
20
networking/websocket_multiplayer/default_env.tres
Normal file
@@ -0,0 +1,20 @@
|
||||
[gd_resource type="Environment" load_steps=2 format=2]
|
||||
|
||||
[sub_resource type="ProceduralSky" id=1]
|
||||
sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 )
|
||||
sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 )
|
||||
sky_curve = 0.25
|
||||
ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 )
|
||||
ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 )
|
||||
ground_curve = 0.01
|
||||
sun_energy = 16.0
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[resource]
|
||||
background_mode = 2
|
||||
background_sky = SubResource( 1 )
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
BIN
networking/websocket_multiplayer/icon.png
Normal file
BIN
networking/websocket_multiplayer/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
networking/websocket_multiplayer/img/crown.png
Normal file
BIN
networking/websocket_multiplayer/img/crown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 776 B |
24
networking/websocket_multiplayer/project.godot
Normal file
24
networking/websocket_multiplayer/project.godot
Normal file
@@ -0,0 +1,24 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=4
|
||||
|
||||
_global_script_classes=[ ]
|
||||
_global_script_class_icons={
|
||||
|
||||
}
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Websocket Multiplayer Demo"
|
||||
run/main_scene="res://scene/main.tscn"
|
||||
config/icon="res://icon.png"
|
||||
|
||||
[rendering]
|
||||
|
||||
environment/default_environment="res://default_env.tres"
|
||||
68
networking/websocket_multiplayer/scene/game.tscn
Normal file
68
networking/websocket_multiplayer/scene/game.tscn
Normal file
@@ -0,0 +1,68 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://script/game.gd" type="Script" id=1]
|
||||
|
||||
[node name="Game" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 1
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="HBoxContainer"]
|
||||
margin_right = 510.0
|
||||
margin_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"]
|
||||
margin_left = 514.0
|
||||
margin_right = 1024.0
|
||||
margin_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="HBoxContainer/VBoxContainer"]
|
||||
margin_right = 510.0
|
||||
margin_bottom = 14.0
|
||||
text = "Players:"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="ItemList" type="ItemList" parent="HBoxContainer/VBoxContainer"]
|
||||
margin_top = 18.0
|
||||
margin_right = 510.0
|
||||
margin_bottom = 576.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
same_column_width = true
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Action" type="Button" parent="HBoxContainer/VBoxContainer"]
|
||||
margin_top = 580.0
|
||||
margin_right = 510.0
|
||||
margin_bottom = 600.0
|
||||
disabled = true
|
||||
text = "Do Action!"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
[connection signal="pressed" from="HBoxContainer/VBoxContainer/Action" to="." method="_on_Action_pressed"]
|
||||
155
networking/websocket_multiplayer/scene/main.tscn
Normal file
155
networking/websocket_multiplayer/scene/main.tscn
Normal file
@@ -0,0 +1,155 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://script/main.gd" type="Script" id=1]
|
||||
[ext_resource path="res://scene/game.tscn" type="PackedScene" id=2]
|
||||
|
||||
[node name="Control" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 20.0
|
||||
margin_top = 20.0
|
||||
margin_right = -20.0
|
||||
margin_bottom = -20.0
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer"]
|
||||
margin_right = 984.0
|
||||
margin_bottom = 24.0
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="Panel/VBoxContainer/HBoxContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 326.0
|
||||
margin_bottom = 19.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Name"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="NameEdit" type="LineEdit" parent="Panel/VBoxContainer/HBoxContainer"]
|
||||
margin_left = 330.0
|
||||
margin_right = 984.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "A Godot User"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Panel/VBoxContainer"]
|
||||
margin_top = 28.0
|
||||
margin_right = 984.0
|
||||
margin_bottom = 52.0
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer/HBoxContainer2"]
|
||||
margin_right = 326.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Host" type="Button" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
|
||||
margin_right = 42.0
|
||||
margin_bottom = 24.0
|
||||
text = "Host"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Control" type="Control" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
|
||||
margin_left = 46.0
|
||||
margin_right = 241.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Connect" type="Button" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
|
||||
margin_left = 245.0
|
||||
margin_right = 326.0
|
||||
margin_bottom = 24.0
|
||||
text = "Connect to"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Disconnect" type="Button" parent="Panel/VBoxContainer/HBoxContainer2/HBoxContainer"]
|
||||
visible = false
|
||||
margin_left = 68.0
|
||||
margin_right = 152.0
|
||||
margin_bottom = 24.0
|
||||
text = "Disconnect"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Hostname" type="LineEdit" parent="Panel/VBoxContainer/HBoxContainer2"]
|
||||
margin_left = 330.0
|
||||
margin_right = 984.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 2.0
|
||||
text = "localhost"
|
||||
placeholder_text = "localhost"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Control" type="Control" parent="Panel/VBoxContainer"]
|
||||
margin_top = 56.0
|
||||
margin_right = 984.0
|
||||
margin_bottom = 76.0
|
||||
rect_min_size = Vector2( 0, 20 )
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
|
||||
[node name="Game" parent="Panel/VBoxContainer" instance=ExtResource( 2 )]
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_top = 80.0
|
||||
margin_right = 984.0
|
||||
margin_bottom = 560.0
|
||||
|
||||
[node name="AcceptDialog" type="AcceptDialog" parent="."]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -200.0
|
||||
margin_top = -100.0
|
||||
margin_right = 200.0
|
||||
margin_bottom = 100.0
|
||||
dialog_text = "Connection closed"
|
||||
__meta__ = {
|
||||
|
||||
}
|
||||
[connection signal="pressed" from="Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Host" to="." method="_on_Host_pressed"]
|
||||
[connection signal="pressed" from="Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Connect" to="." method="_on_Connect_pressed"]
|
||||
[connection signal="pressed" from="Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Disconnect" to="." method="_on_Disconnect_pressed"]
|
||||
104
networking/websocket_multiplayer/script/game.gd
Normal file
104
networking/websocket_multiplayer/script/game.gd
Normal file
@@ -0,0 +1,104 @@
|
||||
extends Control
|
||||
|
||||
const _crown = preload("res://img/crown.png")
|
||||
|
||||
onready var _list = $HBoxContainer/VBoxContainer/ItemList
|
||||
onready var _action = $HBoxContainer/VBoxContainer/Action
|
||||
|
||||
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)
|
||||
|
||||
sync 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))
|
||||
return
|
||||
do_action(action)
|
||||
next_turn()
|
||||
|
||||
sync func do_action(action):
|
||||
var name = _list.get_item_text(_turn)
|
||||
var val = randi() % 100
|
||||
rpc("_log", "%s: %ss %d" % [name, action, val])
|
||||
|
||||
sync func set_turn(turn):
|
||||
_turn = turn
|
||||
if turn >= _players.size():
|
||||
return
|
||||
for i in range(0, _players.size()):
|
||||
if i == turn:
|
||||
_list.set_item_icon(i, _crown)
|
||||
else:
|
||||
_list.set_item_icon(i, null)
|
||||
_action.disabled = _players[turn] != get_tree().get_network_unique_id()
|
||||
|
||||
sync func del_player(id):
|
||||
var pos = _players.find(id)
|
||||
if pos == -1:
|
||||
return
|
||||
_players.remove(pos)
|
||||
_list.remove_item(pos)
|
||||
if _turn > pos:
|
||||
_turn -= 1
|
||||
if get_tree().is_network_server():
|
||||
rpc("set_turn", _turn)
|
||||
|
||||
sync func add_player(id, name=""):
|
||||
_players.append(id)
|
||||
if name == "":
|
||||
_list.add_item("... connecting ...", null, false)
|
||||
else:
|
||||
_list.add_item(name, null, false)
|
||||
|
||||
func get_player_name(pos):
|
||||
if pos < _list.get_item_count():
|
||||
return _list.get_item_text(pos)
|
||||
else:
|
||||
return "Error!"
|
||||
|
||||
func next_turn():
|
||||
_turn += 1
|
||||
if _turn >= _players.size():
|
||||
_turn = 0
|
||||
rpc("set_turn", _turn)
|
||||
|
||||
func start():
|
||||
set_turn(0)
|
||||
|
||||
func stop():
|
||||
_players.clear()
|
||||
_list.clear()
|
||||
_turn = 0
|
||||
_action.disabled = true
|
||||
|
||||
func on_peer_add(id):
|
||||
if not get_tree().is_network_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)
|
||||
|
||||
func on_peer_del(id):
|
||||
if not get_tree().is_network_server():
|
||||
return
|
||||
rpc("del_player", id)
|
||||
|
||||
sync func _log(what):
|
||||
$HBoxContainer/RichTextLabel.add_text(what + "\n")
|
||||
|
||||
func _on_Action_pressed():
|
||||
if get_tree().is_network_server():
|
||||
do_action("roll")
|
||||
next_turn()
|
||||
else:
|
||||
rpc_id(1, "request_action", "roll")
|
||||
73
networking/websocket_multiplayer/script/main.gd
Normal file
73
networking/websocket_multiplayer/script/main.gd
Normal file
@@ -0,0 +1,73 @@
|
||||
extends Control
|
||||
|
||||
const DEF_PORT = 8080
|
||||
const PROTO_NAME = "ludus"
|
||||
|
||||
onready var _host_btn = $Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Host
|
||||
onready var _connect_btn = $Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Connect
|
||||
onready var _disconnect_btn = $Panel/VBoxContainer/HBoxContainer2/HBoxContainer/Disconnect
|
||||
onready var _name_edit = $Panel/VBoxContainer/HBoxContainer/NameEdit
|
||||
onready var _host_edit = $Panel/VBoxContainer/HBoxContainer2/Hostname
|
||||
onready var _game = $Panel/VBoxContainer/Game
|
||||
|
||||
func _ready():
|
||||
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
|
||||
|
||||
func start_game():
|
||||
_host_btn.disabled = true
|
||||
_name_edit.editable = false
|
||||
_host_edit.editable = false
|
||||
_connect_btn.hide()
|
||||
_disconnect_btn.show()
|
||||
_game.start()
|
||||
|
||||
func stop_game():
|
||||
_host_btn.disabled = false
|
||||
_name_edit.editable = true
|
||||
_host_edit.editable = true
|
||||
_disconnect_btn.hide()
|
||||
_connect_btn.show()
|
||||
_game.stop()
|
||||
|
||||
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)
|
||||
|
||||
func _connected():
|
||||
_game.rpc("set_player_name", _name_edit.text)
|
||||
|
||||
func _peer_connected(id):
|
||||
_game.on_peer_add(id)
|
||||
|
||||
func _peer_disconnected(id):
|
||||
_game.on_peer_del(id)
|
||||
|
||||
func _on_Host_pressed():
|
||||
var host = WebSocketServer.new()
|
||||
host.listen(DEF_PORT, PoolStringArray(["ludus"]), true)
|
||||
get_tree().connect("server_disconnected", self, "_close_network")
|
||||
get_tree().set_network_peer(host)
|
||||
_game.add_player(1, _name_edit.text)
|
||||
start_game()
|
||||
|
||||
func _on_Disconnect_pressed():
|
||||
_close_network()
|
||||
|
||||
func _on_Connect_pressed():
|
||||
var host = WebSocketClient.new()
|
||||
host.connect_to_url("ws://" + _host_edit.text + ":" + str(DEF_PORT), PoolStringArray([PROTO_NAME]), true)
|
||||
get_tree().connect("connection_failed", self, "_close_network")
|
||||
get_tree().connect("connected_to_server", self, "_connected")
|
||||
get_tree().set_network_peer(host)
|
||||
start_game()
|
||||
Reference in New Issue
Block a user