Add autoload global script

This commit is contained in:
Daniel Flanagan 2021-11-15 15:43:33 -06:00
parent b915552a64
commit 6de5819595
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
9 changed files with 162 additions and 136 deletions

20
game.gd Normal file
View 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
View 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
View 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
View 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
View file

@ -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()

View file

@ -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"]

View file

@ -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):

View file

@ -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
View file

@ -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;