Skip to content

Commit c77d1fd

Browse files
author
tom
committed
Online 2-4p: sync fix, 60s planning ready gate, lobby polish, p2p_net v1.0.1
Players confirm orders before resolution; client keeps human_camp on state sync. Disconnect grace 45s. Headless E2E and GDScript warning fixes.
1 parent 215b4d1 commit c77d1fd

26 files changed

Lines changed: 1938 additions & 443 deletions

godot/CHANGELOG.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,78 @@ Format des entrées : `AAAA-MM-JJ HH:MM:SS` (heure locale, fuseau du commit Git
88

99
---
1010

11+
## 2026-06-09 (sync) — Timer online 60 s + confirmation conjointe
12+
13+
### Planification online
14+
- **60 s partagés** par manche (`GameSession.NETWORK_PLANNING_SEC`) — compte à rebours visible, pas 60 s « par joueur ».
15+
- Solo / IA : toujours **3600 s** (pas de limite pratique).
16+
- Bouton **Confirm orders** pour tous les joueurs online ; la manche ne se résout que quand **tout le monde a confirmé** ou à **0 s**.
17+
- Fix sync : le client conserve son camp (`human_camp`) après réception de l'état hôte (Bleu n'était plus Bleu).
18+
19+
### Déconnexion
20+
- Grâce reconnexion **45 s** (était 90 s) — assez pour un crash/WiFi, sans bloquer la partie trop longtemps.
21+
22+
---
23+
24+
### Tests
25+
- **`full_match_test.gd`** : snapshot de round pris **après** placement des pièces (fix `tie: blue must bounce`).
26+
- Suite headless : **smoke**, **regression**, **compile**, **p2p**, **ForcesNet E2E 2p/4p** — tout OK.
27+
28+
### Interface online
29+
- **Lobby réseau** : logo FORCES, slots joueurs par camp (couleur + statut Connected/Waiting/AI), bouton **Copy** du code, boutons Host/Join côte à côte, **Start match** mis en avant.
30+
- **Bataille** : barre **Online** en haut (camps connectés / away), overlay déconnexion avec titre coloré, barre de progression 90 s, texte type chess.com.
31+
- Menu principal : **Online multiplayer** (2–4 joueurs).
32+
33+
---
34+
35+
### `p2p_net` v1.0.1 (addon générique — **pas encore publié**)
36+
- **`ensure_local_signaling()`** — démarre le serveur WS embarqué sur le port 8080 (plus de terminal séparé pour le dev local).
37+
- Timeout signaling **15 s** (était 1 s — coupait les connexions lentes en éditeur).
38+
- Messages d’erreur explicites si le signaling est injoignable.
39+
- **`tools/setup_webrtc.ps1`** — télécharge webrtc-native Windows.
40+
- **`tools/sync_godot_p2p_net.ps1`** — sync local vers `godot-p2p-net` sans push.
41+
- **`assetlib/PUBLISH_PENDING.md`** — checklist publication (en attente accord Tom).
42+
43+
### Forces online
44+
- Lobby : signaling prêt à l’ouverture ; guest voit « Connected to host — waiting for Start… ».
45+
- **`ForcesNet`** : ignore les messages non-JSON (tests P2P `ping`).
46+
47+
---
48+
49+
## 2026-06-09 (suite) — Online 2–4 joueurs + E2E headless
50+
51+
### Réseau Forces (2 → 4 joueurs)
52+
- **`GameSession`** : `network_player_count`, `network_peer_camps`, assignation auto Blue/Red/Yellow à la connexion.
53+
- Lobby : sélecteur **2 / 3 / 4 players** ; Start quand tous les remotes sont connectés (WebRTC prêt).
54+
- **`ForcesNet.send_match_start()`** : config `peer_camps` + `player_count` ; clients appliquent camp via `apply_network_match_config`.
55+
- **`try_apply_network_order`** : host accepte ordres de tous les camps `NETWORK` (pas seulement Bleu).
56+
- Fix **`restore_from_snapshot`** : `pieces_moved_this_round` typé `Array[int]`.
57+
58+
### Tests E2E
59+
- **`scenes/tests/forces_net_e2e_test.tscn`** : match_start → ordre distant → snapshot manche 2.
60+
- **`tools/run_forces_net_e2e.ps1 -Players 2|4`** ; inclus dans `run_headless.ps1 -Mode p2p`.
61+
62+
### Publication — **STOP** (inchangé)
63+
64+
---
65+
66+
## 2026-06-09 (reconnect) — Déconnexion / reconnexion style chess.com
67+
68+
### Comportement
69+
- **90 s** de grâce (`GameSession.DISCONNECT_GRACE_SEC`) avant abandon auto.
70+
- Timer de planification **gelé** pendant qu’un adversaire est déconnecté.
71+
- Overlay bataille : compte à rebours + bouton **Rejoin match** si tu es déconnecté.
72+
- Lobby : bouton **Rejoin previous match** si checkpoint valide.
73+
- Host envoie **resync** (snapshot complet) au joueur qui revient.
74+
- Signaling **reste ouvert** après `seal_lobby` pour permettre le re-Join WebRTC.
75+
76+
### Protocole ForcesNet
77+
- `disconnect`, `reconnect`, `forfeit`, `resync` (JSON sur P2PNet).
78+
79+
### p2p_net
80+
- `P2PNet.rejoin_room(code)` + `Lobby.rejoin()` côté serveur signaling.
81+
82+
1183
## 2026-05-29 — Couche réseau Forces (`ForcesNet`, MVP 2 joueurs)
1284

1385
### Intégration au-dessus de `P2PNet` (pas dans l’addon)

godot/addons/p2p_net/README.md

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,90 @@
1-
# p2p_net — addon réseau Godot 4
1+
# p2p_net — P2P networking addon for Godot 4
22

3-
Couche **transport P2P** jeu-agnostique : signaling WebSocket + WebRTC mesh, API bytes bruts.
3+
Game-agnostic **transport layer**: WebSocket signaling + WebRTC mesh, raw byte messages.
44

5-
## Prérequis
5+
## Requirements
66

7-
- Godot **4.6+**
8-
- Desktop : [webrtc-native](https://github.com/godotengine/webrtc-native) dans `res://webrtc/` (GDExtension)
9-
- HTML5 : WebRTC intégré (pas de GDExtension)
7+
- Godot **4.3+** (tested on 4.6)
8+
- **Desktop**: [webrtc-native](https://github.com/godotengine/webrtc-native) GDExtension in `res://webrtc/`
9+
- **HTML5**: built-in WebRTC (no extension)
1010

11-
Première ouverture ou CI :
11+
After installing webrtc-native (Windows: `.\tools\setup_webrtc.ps1` in Forces, or extract [godot-extension-webrtc.zip](https://github.com/godotengine/webrtc-native/releases) into project root):
1212

13-
```powershell
13+
```text
1414
godot --headless --path . --import --quit
1515
```
1616

1717
## Installation
1818

19-
1. Copier `addons/p2p_net/` dans le projet
20-
2. Activer le plugin **P2P Net** (Project → Project Settings → Plugins)
21-
3. L’autoload `P2PNet` est enregistré automatiquement
19+
1. Copy `addons/p2p_net/` into your project
20+
2. Enable **Project → Project Settings → Plugins → P2P Net**
21+
3. Autoload `P2PNet` is registered automatically
2222

23-
## API (`P2PNet`)
23+
## Quick start
2424

25-
| Méthode | Description |
26-
|---------|-------------|
27-
| `configure(config)` | `net_config.gd` : URL signaling, STUN/TURN, max peers |
28-
| `host_room(max_peers)` | Crée une salle (host = peer id `1` en mesh) |
29-
| `join_room(code)` | Rejoint une salle existante |
30-
| `seal_lobby()` | Host only — ferme le lobby signaling |
31-
| `leave()` | Quitte proprement (sans faux `connection_failed`) |
32-
| `send_to(id, bytes, reliable)` |`Error` |
33-
| `broadcast(bytes, reliable)` |`Error` |
34-
| `room_code()` / `is_in_room()` / `is_host()` / `my_peer_id()` / `peer_ids()` | État |
25+
```gdscript
26+
# Configure (optional)
27+
var cfg = load("res://addons/p2p_net/net_config.gd").defaults()
28+
cfg.signaling_url = "ws://127.0.0.1:8080"
29+
P2PNet.configure(cfg)
3530
36-
### Signaux
31+
# Host — starts local signaling automatically if port 8080 is free
32+
P2PNet.ensure_local_signaling()
33+
P2PNet.room_ready.connect(func(code): print("Room: ", code))
34+
P2PNet.host_room(4)
3735
38-
`room_ready`, `room_sealed`, `room_left`, `peer_joined`, `peer_left`, `message_received`, `connection_failed`
36+
# Join
37+
P2PNet.join_room("YOUR_ROOM_CODE")
3938
40-
`peer_joined` = WebRTC data channels prêts (pas seulement signaling).
39+
# When peer_joined fires (WebRTC ready):
40+
P2PNet.send_to(peer_id, "hello".to_utf8_buffer())
41+
P2PNet.message_received.connect(func(from_id, data): print(data.get_string_from_utf8()))
42+
```
4143

42-
## Configuration TURN (optionnel)
44+
## Local signaling server
4345

44-
```gdscript
45-
var cfg = load("res://addons/p2p_net/net_config.gd").defaults()
46-
cfg.turn_url = "turn:your.server:3478"
47-
cfg.turn_username = "user"
48-
cfg.turn_password = "pass"
49-
P2PNet.configure(cfg)
50-
```
46+
**Automatic (recommended):** call `P2PNet.ensure_local_signaling()` before `host_room()` — listens on `ws://127.0.0.1:8080` inside the game process. If the port is already in use, an external server is assumed (second game instance, dedicated server, etc.).
5147

52-
## Serveur signaling local
48+
**Manual (optional):**
5349

54-
```powershell
50+
```text
5551
godot --headless --path . res://addons/p2p_net/server/signaling_server.tscn
5652
```
5753

58-
Écoute `ws://127.0.0.1:8080`. Protocole compatible [demo officielle Godot `webrtc_signaling`](https://github.com/godotengine/godot-demo-projects/tree/master/networking/webrtc_signaling).
54+
## API summary
5955

60-
`max_peers` est transmis à la création de salle et enforced côté serveur.
56+
| Method | Description |
57+
|--------|-------------|
58+
| `ensure_local_signaling()` | Start embedded signaling on `config.signaling_url` port (desktop) |
59+
| `host_room(max_peers)` | Create a room (auto-starts local signaling) |
60+
| `join_room(code)` | Join existing room |
61+
| `seal_lobby()` | Host closes signaling lobby |
62+
| `leave()` | Clean disconnect |
63+
| `send_to(id, bytes, reliable)` | Returns `Error` |
64+
| `broadcast(bytes, reliable)` | Returns `Error` |
65+
| `room_code()` / `is_in_room()` / `is_host()` | State |
6166

62-
## Tests
67+
**Signals:** `room_ready`, `room_sealed`, `room_left`, `peer_joined`, `peer_left`, `message_received`, `connection_failed`
6368

64-
```powershell
65-
cd godot
66-
.\tools\run_p2p_smoke.ps1 # 2 joueurs
67-
.\tools\run_p2p_all.ps1 # suite V1 complète
68-
.\tools\run_headless.ps1 -Mode p2p
69-
```
69+
`peer_joined` means WebRTC data channels are ready, not just signaling.
7070

71-
Suite `run_p2p_all` : import GDExtension, salle invalide, smoke 2p, salle pleine, mesh 4p, `seal_lobby`.
71+
## TURN (optional)
7272

73-
## Backends
73+
```gdscript
74+
cfg.turn_url = "turn:your.server:3478"
75+
cfg.turn_username = "user"
76+
cfg.turn_password = "pass"
77+
```
7478

75-
| Backend | État |
76-
|---------|------|
77-
| `WEBRTC_SIGNALING` | **V1.0** |
78-
| `JANUS` | stub |
79+
## Tests (optional)
7980

80-
## Hors scope addon
81+
```powershell
82+
.\tools\setup_webrtc.ps1 # Windows — download webrtc-native once
83+
.\tools\run_headless.ps1 -Mode p2p
84+
```
8185

82-
- Règles de jeu Forces (`GameOrder`, sync état) → couche `forces_net_adapter` séparée
83-
- UI lobby / matchmaking
86+
Headless smoke tests live in `addons/p2p_net/test/`. They require webrtc-native on desktop.
8487

85-
## Version
88+
## License
8689

87-
`1.0.0`voir `plugin.cfg`
90+
MITsee repository root `LICENSE`.

godot/addons/p2p_net/p2p_net.gd

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ signal connection_failed(reason: String)
1414
const NetConfigScript = preload("res://addons/p2p_net/net_config.gd")
1515
const WebRtcMeshTransportScript = preload("res://addons/p2p_net/transport/webrtc_mesh_transport.gd")
1616
const JanusTransportScript = preload("res://addons/p2p_net/transport/janus_transport.gd")
17+
const SignalingServerScript = preload("res://addons/p2p_net/server/ws_signaling_server.gd")
1718

1819
var config # P2PNetConfig
1920
var _transport # P2PTransportBackend
2021
var _backend_kind: Backend = Backend.WEBRTC_SIGNALING
22+
var _local_signaling: Node
2123

2224

2325
func _ready() -> void:
@@ -62,14 +64,38 @@ func _wire_transport() -> void:
6264
_transport.connection_failed.connect(func(reason: String) -> void: connection_failed.emit(reason))
6365

6466

67+
func ensure_local_signaling() -> bool:
68+
if OS.has_feature("web"):
69+
return false
70+
if _local_signaling != null and is_instance_valid(_local_signaling):
71+
return _local_signaling.is_listening()
72+
_local_signaling = SignalingServerScript.new()
73+
_local_signaling.name = "LocalSignaling"
74+
add_child(_local_signaling)
75+
return _local_signaling.is_listening()
76+
77+
6578
func host_room(max_peers: int = 4) -> void:
79+
if not ensure_local_signaling():
80+
connection_failed.emit(
81+
"Local signaling server unavailable (port 8080). Close other apps using it."
82+
)
83+
return
6684
if _transport:
6785
_transport.host_room(max_peers)
6886

6987

70-
func join_room(room_code: String) -> void:
88+
func join_room(code: String) -> void:
89+
if _transport:
90+
_transport.join_room(code)
91+
92+
93+
func rejoin_room(code: String) -> void:
94+
if not ensure_local_signaling():
95+
connection_failed.emit("Signaling unavailable for rejoin.")
96+
return
7197
if _transport:
72-
_transport.join_room(room_code)
98+
_transport.rejoin_room(code)
7399

74100

75101
func seal_lobby() -> Error:

godot/addons/p2p_net/plugin.cfg

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[plugin]
22

33
name="P2P Net"
4-
description="Couche réseau générique : signaling WebSocket + WebRTC mesh, API bytes bruts (jeu-agnostique)."
5-
author="Forces"
6-
version="1.0.0"
4+
description="Generic P2P layer: WebSocket signaling + WebRTC mesh, raw bytes API (game-agnostic)."
5+
author="Tom"
6+
version="1.0.1"
77
script="plugin.gd"

0 commit comments

Comments
 (0)