Add autoload global script
This commit is contained in:
parent
b915552a64
commit
6de5819595
9 changed files with 162 additions and 136 deletions
20
game.gd
Normal file
20
game.gd
Normal file
|
@ -0,0 +1,20 @@
|
|||
extends Node2D
|
||||
|
||||
|
||||
# Declare member variables here. Examples:
|
||||
# var a = 2
|
||||
# var b = "text"
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
#func _process(delta):
|
||||
# pass
|
||||
|
||||
|
||||
func _on_Button_pressed():
|
||||
Global.leave_game()
|
28
game.tscn
Normal file
28
game.tscn
Normal file
|
@ -0,0 +1,28 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://game.gd" type="Script" id=1]
|
||||
|
||||
[node name="Node2D" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
margin_left = 93.0
|
||||
margin_top = 109.0
|
||||
margin_right = 253.0
|
||||
margin_bottom = 123.0
|
||||
text = "You are playing the game"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Button" type="Button" parent="Label"]
|
||||
margin_left = 29.0
|
||||
margin_top = 177.0
|
||||
margin_right = 120.0
|
||||
margin_bottom = 197.0
|
||||
text = "Leave Game"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[connection signal="pressed" from="Label/Button" to="." method="_on_Button_pressed"]
|
20
gen.ts
Normal file
20
gen.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
const ALFNUM = "0123456789";
|
||||
const SECRET_LENGTH = 8;
|
||||
|
||||
function randomInt(low: number, high: number) {
|
||||
return Math.floor(Math.random() * (high - low + 1) + low);
|
||||
}
|
||||
|
||||
export function randomId() {
|
||||
const arr = new Int32Array(1);
|
||||
crypto.getRandomValues(arr);
|
||||
return Math.abs(arr[0]);
|
||||
}
|
||||
|
||||
export function randomSecret() {
|
||||
let out = "";
|
||||
for (let i = 0; i < SECRET_LENGTH; i++) {
|
||||
out += ALFNUM[randomInt(0, ALFNUM.length - 1)];
|
||||
}
|
||||
return out;
|
||||
}
|
22
global.gd
Normal file
22
global.gd
Normal file
|
@ -0,0 +1,22 @@
|
|||
extends Node
|
||||
|
||||
|
||||
# Declare member variables here. Examples:
|
||||
# var a = 2
|
||||
# var b = "text"
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
#func _process(delta):
|
||||
# pass
|
||||
|
||||
func start_singleplayer_game():
|
||||
print("Starting singleplayer game...")
|
||||
|
||||
func leave_game():
|
||||
print("Leaving game...")
|
14
main.gd
14
main.gd
|
@ -1,14 +1,4 @@
|
|||
extends Control
|
||||
|
||||
func _ready():
|
||||
if OS.get_name() == "HTML5":
|
||||
$VBoxContainer/Signaling.hide()
|
||||
|
||||
func _on_listen_toggled(button_pressed):
|
||||
if button_pressed:
|
||||
$Server.listen(int($VBoxContainer/Signaling/Port.value))
|
||||
else:
|
||||
$Server.stop()
|
||||
|
||||
func _on_LinkButton_pressed():
|
||||
OS.shell_open("https://github.com/godotengine/webrtc-native/releases")
|
||||
func _on_Singleplayer_pressed():
|
||||
Global.start_singleplayer_game()
|
||||
|
|
86
main.tscn
86
main.tscn
|
@ -1,7 +1,6 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://main.gd" type="Script" id=1]
|
||||
[ext_resource path="res://client_ui.tscn" type="PackedScene" id=2]
|
||||
|
||||
[node name="Control" type="Control"]
|
||||
anchor_left = 0.0136719
|
||||
|
@ -23,76 +22,29 @@ __meta__ = {
|
|||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="Signaling" type="HBoxContainer" parent="VBoxContainer"]
|
||||
[node name="Singleplayer" type="Button" parent="VBoxContainer"]
|
||||
margin_right = 995.0
|
||||
margin_bottom = 24.0
|
||||
margin_bottom = 20.0
|
||||
text = "Start Singleplayer"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/Signaling"]
|
||||
margin_top = 5.0
|
||||
margin_right = 104.0
|
||||
margin_bottom = 19.0
|
||||
text = "Signaling server:"
|
||||
|
||||
[node name="Port" type="SpinBox" parent="VBoxContainer/Signaling"]
|
||||
margin_left = 108.0
|
||||
margin_right = 182.0
|
||||
margin_bottom = 24.0
|
||||
min_value = 1025.0
|
||||
max_value = 65535.0
|
||||
value = 9080.0
|
||||
|
||||
[node name="ListenButton" type="Button" parent="VBoxContainer/Signaling"]
|
||||
margin_left = 186.0
|
||||
margin_right = 237.0
|
||||
margin_bottom = 24.0
|
||||
toggle_mode = true
|
||||
text = "Listen"
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/Signaling"]
|
||||
margin_left = 241.0
|
||||
[node name="CreateLobbyButton" type="Button" parent="VBoxContainer"]
|
||||
margin_top = 70.0
|
||||
margin_right = 995.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
margin_bottom = 90.0
|
||||
text = "Create Lobby"
|
||||
|
||||
[node name="LinkButton" type="LinkButton" parent="VBoxContainer/Signaling/CenterContainer"]
|
||||
margin_left = 104.0
|
||||
margin_top = 5.0
|
||||
margin_right = 650.0
|
||||
margin_bottom = 19.0
|
||||
text = "Make sure to download the GDNative WebRTC Plugin and place it in the project folder"
|
||||
|
||||
[node name="Clients" type="GridContainer" parent="VBoxContainer"]
|
||||
margin_top = 74.0
|
||||
[node name="JoinLobbyButton" type="Button" parent="VBoxContainer"]
|
||||
margin_top = 140.0
|
||||
margin_right = 995.0
|
||||
margin_bottom = 579.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
custom_constants/vseparation = 15
|
||||
custom_constants/hseparation = 15
|
||||
columns = 2
|
||||
|
||||
[node name="ClientUI" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||
margin_right = 490.0
|
||||
margin_bottom = 245.0
|
||||
|
||||
[node name="ClientUI2" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||
margin_left = 505.0
|
||||
margin_right = 995.0
|
||||
margin_bottom = 245.0
|
||||
|
||||
[node name="ClientUI3" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||
margin_top = 260.0
|
||||
margin_right = 490.0
|
||||
margin_bottom = 505.0
|
||||
|
||||
[node name="ClientUI4" parent="VBoxContainer/Clients" instance=ExtResource( 2 )]
|
||||
margin_left = 505.0
|
||||
margin_top = 260.0
|
||||
margin_right = 995.0
|
||||
margin_bottom = 505.0
|
||||
margin_bottom = 160.0
|
||||
text = "Join Lobby"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Server" type="Node" parent="."]
|
||||
|
||||
[connection signal="toggled" from="VBoxContainer/Signaling/ListenButton" to="." method="_on_listen_toggled"]
|
||||
[connection signal="pressed" from="VBoxContainer/Signaling/CenterContainer/LinkButton" to="." method="_on_LinkButton_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/Singleplayer" to="." method="_on_Singleplayer_pressed"]
|
||||
|
|
|
@ -39,6 +39,7 @@ func _create_peer(id):
|
|||
return peer
|
||||
|
||||
func _new_ice_candidate(mid_name, index_name, sdp_name, id):
|
||||
print("New ICE Candidate: ", mid_name, index_name, sdp_name, id)
|
||||
send_candidate(id, mid_name, index_name, sdp_name)
|
||||
|
||||
func _offer_created(type, data, id):
|
||||
|
|
|
@ -14,6 +14,10 @@ config/name="kdt"
|
|||
run/main_scene="res://main.tscn"
|
||||
config/icon="res://icon.png"
|
||||
|
||||
[autoload]
|
||||
|
||||
Global="*res://global.gd"
|
||||
|
||||
[gdnative]
|
||||
|
||||
singletons=[ "res://webrtc/webrtc.tres" ]
|
||||
|
|
103
server.ts
103
server.ts
|
@ -1,11 +1,12 @@
|
|||
import { randomId, randomSecret } from "./gen.ts";
|
||||
|
||||
const MAX_PEERS = 4096;
|
||||
const MAX_LOBBIES = 1024;
|
||||
const PORT = 9080;
|
||||
const ALFNUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
const NO_LOBBY_TIMEOUT = 1000;
|
||||
const NO_LOBBY_TIMEOUT = 10000;
|
||||
const SEAL_CLOSE_TIMEOUT = 10000;
|
||||
const PING_INTERVAL = 10000;
|
||||
// const PING_INTERVAL = 10000;
|
||||
|
||||
const STR_NO_LOBBY = "Have not joined lobby yet";
|
||||
const STR_HOST_DISCONNECTED = "Room host has disconnected";
|
||||
|
@ -22,23 +23,11 @@ const STR_INVALID_DEST = "Invalid destination";
|
|||
const STR_INVALID_CMD = "Invalid command";
|
||||
const STR_TOO_MANY_PEERS = "Too many peers connected";
|
||||
|
||||
function randomInt(low: number, high: number) {
|
||||
return Math.floor(Math.random() * (high - low + 1) + low);
|
||||
}
|
||||
// TODO: setup regular pings?
|
||||
|
||||
function randomId() {
|
||||
const arr = new Int32Array(1);
|
||||
crypto.getRandomValues(arr);
|
||||
return Math.abs(arr[0]);
|
||||
}
|
||||
|
||||
function randomSecret() {
|
||||
let out = "";
|
||||
for (let i = 0; i < 16; i++) {
|
||||
out += ALFNUM[randomInt(0, ALFNUM.length - 1)];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
// state
|
||||
const lobbies = new Map();
|
||||
let peersCount = 0;
|
||||
|
||||
class ProtoError extends Error {
|
||||
code: number;
|
||||
|
@ -59,11 +48,38 @@ class Peer {
|
|||
this.id = id;
|
||||
this.ws = ws;
|
||||
this.lobby = "";
|
||||
// Close connection after 1 sec if client has not joined a lobby
|
||||
|
||||
// close connection after 10 sec if client has not joined a lobby
|
||||
this.timeout = setTimeout(() => {
|
||||
if (!this.lobby) ws.close(4000, STR_NO_LOBBY);
|
||||
}, NO_LOBBY_TIMEOUT);
|
||||
}
|
||||
|
||||
joinLobby(lobbyName: string) {
|
||||
if (lobbyName === "") {
|
||||
if (lobbies.size >= MAX_LOBBIES) {
|
||||
throw new ProtoError(4000, STR_TOO_MANY_LOBBIES);
|
||||
}
|
||||
// Peer must not already be in a lobby
|
||||
if (this.lobby !== "") {
|
||||
throw new ProtoError(4000, STR_ALREADY_IN_LOBBY);
|
||||
}
|
||||
lobbyName = randomSecret();
|
||||
lobbies.set(lobbyName, new Lobby(lobbyName, this.id));
|
||||
console.log(`Peer ${this.id} created lobby ${lobbyName}`);
|
||||
console.log(`Open lobbies: ${lobbies.size}`);
|
||||
}
|
||||
const lobby = lobbies.get(lobbyName);
|
||||
if (!lobby) throw new ProtoError(4000, STR_LOBBY_DOES_NOT_EXISTS);
|
||||
if (lobby.sealed) throw new ProtoError(4000, STR_LOBBY_IS_SEALED);
|
||||
this.lobby = lobbyName;
|
||||
console.log(
|
||||
`Peer ${this.id} joining lobby ${lobbyName} ` +
|
||||
`with ${lobby.peers.length} peers`,
|
||||
);
|
||||
lobby.join(this);
|
||||
this.ws.send(`J: ${lobbyName}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
class Lobby {
|
||||
|
@ -95,6 +111,7 @@ class Lobby {
|
|||
});
|
||||
this.peers.push(peer);
|
||||
}
|
||||
|
||||
leave(peer: Peer) {
|
||||
const idx = this.peers.findIndex((p) => peer === p);
|
||||
if (idx === -1) return false;
|
||||
|
@ -102,9 +119,9 @@ class Lobby {
|
|||
const close = assigned === 1;
|
||||
this.peers.forEach((p) => {
|
||||
try {
|
||||
// Room host disconnected, must close.
|
||||
// room host disconnected
|
||||
if (close) p.ws.close(4000, STR_HOST_DISCONNECTED);
|
||||
// Notify peer disconnect.
|
||||
// notify peers
|
||||
else p.ws.send(`D: ${assigned}\n`);
|
||||
} catch (e) {
|
||||
console.error(`Error when leaving: ${e}`);
|
||||
|
@ -112,14 +129,15 @@ class Lobby {
|
|||
});
|
||||
this.peers.splice(idx, 1);
|
||||
if (close && this.closeTimer >= 0) {
|
||||
// We are closing already.
|
||||
// we are closing already.
|
||||
clearTimeout(this.closeTimer);
|
||||
this.closeTimer = -1;
|
||||
}
|
||||
return close;
|
||||
}
|
||||
|
||||
seal(peer: Peer) {
|
||||
// Only host can seal
|
||||
// only host can seal
|
||||
if (peer.id !== this.host) {
|
||||
throw new ProtoError(4000, STR_ONLY_HOST_CAN_SEAL);
|
||||
}
|
||||
|
@ -132,7 +150,7 @@ class Lobby {
|
|||
`with ${this.peers.length} peers`,
|
||||
);
|
||||
this.closeTimer = setTimeout(() => {
|
||||
// Close peer connection to host (and thus the lobby)
|
||||
// close peer connection to host (and thus the lobby)
|
||||
this.peers.forEach((p) => {
|
||||
p.ws.close(1000, STR_SEAL_COMPLETE);
|
||||
});
|
||||
|
@ -140,35 +158,6 @@ class Lobby {
|
|||
}
|
||||
}
|
||||
|
||||
const lobbies = new Map();
|
||||
let peersCount = 0;
|
||||
|
||||
function joinLobby(peer: Peer, lobbyName: string) {
|
||||
if (lobbyName === "") {
|
||||
if (lobbies.size >= MAX_LOBBIES) {
|
||||
throw new ProtoError(4000, STR_TOO_MANY_LOBBIES);
|
||||
}
|
||||
// Peer must not already be in a lobby
|
||||
if (peer.lobby !== "") {
|
||||
throw new ProtoError(4000, STR_ALREADY_IN_LOBBY);
|
||||
}
|
||||
lobbyName = randomSecret();
|
||||
lobbies.set(lobbyName, new Lobby(lobbyName, peer.id));
|
||||
console.log(`Peer ${peer.id} created lobby ${lobbyName}`);
|
||||
console.log(`Open lobbies: ${lobbies.size}`);
|
||||
}
|
||||
const lobby = lobbies.get(lobbyName);
|
||||
if (!lobby) throw new ProtoError(4000, STR_LOBBY_DOES_NOT_EXISTS);
|
||||
if (lobby.sealed) throw new ProtoError(4000, STR_LOBBY_IS_SEALED);
|
||||
peer.lobby = lobbyName;
|
||||
console.log(
|
||||
`Peer ${peer.id} joining lobby ${lobbyName} ` +
|
||||
`with ${lobby.peers.length} peers`,
|
||||
);
|
||||
lobby.join(peer);
|
||||
peer.ws.send(`J: ${lobbyName}\n`);
|
||||
}
|
||||
|
||||
function parseMsg(peer: Peer, msg: string) {
|
||||
const sep = msg.indexOf("\n");
|
||||
if (sep < 0) throw new ProtoError(4000, STR_INVALID_FORMAT);
|
||||
|
@ -178,9 +167,9 @@ function parseMsg(peer: Peer, msg: string) {
|
|||
|
||||
const data = msg.slice(sep);
|
||||
|
||||
// Lobby joining.
|
||||
// join
|
||||
if (cmd.startsWith("J: ")) {
|
||||
joinLobby(peer, cmd.substr(3).trim());
|
||||
peer.joinLobby(cmd.substr(3).trim());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -188,7 +177,7 @@ function parseMsg(peer: Peer, msg: string) {
|
|||
const lobby = lobbies.get(peer.lobby);
|
||||
if (!lobby) throw new ProtoError(4000, STR_SERVER_ERROR);
|
||||
|
||||
// Lobby sealing.
|
||||
// seal
|
||||
if (cmd.startsWith("S: ")) {
|
||||
lobby.seal(peer);
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue