diff --git a/automated_updates_data.json b/automated_updates_data.json index 411c7c5347..776f946e7c 100644 --- a/automated_updates_data.json +++ b/automated_updates_data.json @@ -88,6 +88,10 @@ { "date": "2026-04-02", "summary": "Improved multiplayer docs: added Quick Join section, documented player username/ping/last-joined/last-left expressions, lobby ID expression, custom message variable variant, and synchronization rate action" + }, + { + "date": "2026-04-11", + "summary": "Improved P2P docs (added connection lifecycle events, variable data sending, disconnect actions, ICE/relay server config, expressions) and Save State docs (added time-tracking expressions, save/load monitoring conditions, stop-and-restart parameter)" } ] } diff --git a/docs/gdevelop5/all-features/p2p/index.md b/docs/gdevelop5/all-features/p2p/index.md index c348c0774a..db6a05f18d 100644 --- a/docs/gdevelop5/all-features/p2p/index.md +++ b/docs/gdevelop5/all-features/p2p/index.md @@ -65,11 +65,11 @@ To use that server use the action "Use the default server". ## Connecting -To connect instances, you need to enter their ID in the other instances. The ID can be found with the expression `P2P::GetID()`. To connect, use the "Connect to other instance" action and pass as a parameter the ID of another instance. Both instances will then connect automatically. You can then send an event from one instance to the other one to make sure that the connection is established. +To connect instances, you need to enter their ID in the other instances. The ID can be found with the expression `P2P::GetID()`. Wait until the **Is P2P ready?** condition is true before reading this expression — the ID is only available once the broker connection is established. To connect, use the "Connect to other instance" action and pass as a parameter the ID of another instance. Both instances will then connect automatically. You can then send an event from one instance to the other one to make sure that the connection is established. ### Changing the ID generation -The default P2P ID generation is very long to avoid conflicts, but if you want to have an easily shareable ID, it is not ideal. You can use a custom ID generation on your custom P2P broker by following [the instructions on the peerjs-server documentation](https://github.com/peers/peerjs-server#custom-client-id-generation). +The default P2P ID generation is very long to avoid conflicts, but if you want to have an easily shareable ID, it is not ideal. You can use a custom ID generation on your custom P2P broker by following [the instructions on the peerjs-server documentation](https://github.com/peers/peerjs-server#custom-client-id-generation). You can also use the **Override the client ID** action (called before connecting to the broker) to set a specific ID from the game itself — see the Advanced configuration section below. ## Interacting with connected games @@ -89,6 +89,56 @@ Here are two examples: * if you use a synchronized score counter, you don't want to lose any data, as missing only one point of the counter would *desynchronize* the counters, so the dataloss mode would be deactivated. * If you want to synchronize positions, only the last position sent is relevant, not older positions. In this case, you would activate the dataloss mode *to prevent delays/lags*. +### Sending complex data with variables + +The basic **Send to all** and **Send to one** actions attach a single string of extra data to an event. If you need to send structured data (for example, a player's position along with their health as a structure variable), use the **variable** variants instead: **Send to all (variable)** and **Send to one (variable)**. These encode the variable's full content (including nested structures and arrays). + +On the receiving end, use the **Get event variable** action to decode the received data directly into a local variable, rather than using `P2P::GetEventData()` with manual string parsing. + +### Reading event data with expressions + +When you receive a remote event, you can retrieve the extra data sent alongside it using expressions: + +- `P2P::GetEventData("EventName")` — returns the string data attached to the last received occurrence of that event. +- `P2P::GetEventSender("EventName")` — returns the ID of the peer who triggered that event, useful when you need to reply to a specific peer. + +## Reacting to connections and disconnections + +Use the **P2P peer connected** condition to run logic whenever a new peer connects to your game (for example, to welcome them or share initial game state). Pair it with `P2P::GetLastConnectedPeer()` to get the ID of who just connected. + +Similarly, the **P2P peer disconnected** condition triggers when a peer leaves. Use `P2P::GetLastDisconnectedPeer()` to find out which peer disconnected, so you can clean up their objects or notify remaining players. + +The **Is P2P ready?** condition becomes true once the broker connection is established and your game has received its ID. Wait for this condition before displaying your `P2P::GetID()` to players or initiating connections to peers. + +### Handling errors + +If something goes wrong (broker unreachable, ICE failure, etc.), the **P2P error occurred** condition will trigger. Use `P2P::GetLastError()` to retrieve the error description and display it to the player or log it for debugging. + +## Disconnecting + +You can cleanly disconnect from the P2P network at any time: + +- **Disconnect from a peer** — drop the connection to one specific peer by ID, while staying connected to others. +- **Disconnect from all peers** — close all peer-to-peer connections but remain registered on the broker server (so new peers can still connect to you). +- **Disconnect from broker** — unregister from the broker without closing existing peer connections. Useful if you want to stop accepting new connections mid-game while keeping active sessions alive. +- **Disconnect from all** — disconnect from both the broker and all peers at once, fully leaving the P2P session. + +## Advanced configuration + +The following actions must be called **before** connecting to the broker server. + +### Override the client ID + +By default, each game instance receives a randomly generated, long ID when it connects to the broker. You can set a custom ID using the **Override the client ID** action. This is useful for creating lobby systems where players share a short, memorable code, though the broker server must be configured to allow custom IDs. + +### Custom ICE servers (STUN/TURN) + +P2P connections use ICE servers to help peers establish a connection across firewalls and NAT. You can add your own STUN or TURN server with the **Use a custom ICE server** action (providing the server URL, and optionally a username and password). Call this action multiple times to configure several ICE servers. + +### Prevent IP address leaking with a relay server + +By default, a direct connection is attempted between peers, which exposes their IP addresses to each other. If you want to route all traffic through a TURN relay server instead — completely hiding peer IP addresses — enable the **Force use of relay server** option. This requires a TURN server (set up via the custom ICE server action). When this mode is active, connections that cannot be relayed will fail, so make sure your TURN server is reachable. + ## Reference All actions, conditions and expressions are listed in [the peer-to-peer reference page](/gdevelop5/all-features/p2p/reference/). \ No newline at end of file diff --git a/docs/gdevelop5/all-features/save-state/index.md b/docs/gdevelop5/all-features/save-state/index.md index 06b27a058a..5d629fce3e 100644 --- a/docs/gdevelop5/all-features/save-state/index.md +++ b/docs/gdevelop5/all-features/save-state/index.md @@ -36,6 +36,7 @@ For this, use actions **Save game to device storage** and **Load game from devic Each save uses a **storage key**, such as `"Save1"`, `"CheckpointA"`, or `"Autosave"`, to identify the save slot. This enables you to offer multiple save slots (in some games, it's usual to have 3 to 5 save slots that the player can use). +The load action has an optional **Stop and restart all scenes** parameter. When enabled, all currently running scenes are stopped and restarted before the state is applied — useful when loading a save from the main menu where the gameplay scenes aren't running yet. When disabled (the default), the state is applied on top of the currently running scenes. !!! tip @@ -56,7 +57,12 @@ This is useful for: ## Monitoring Save/Load Operations -The extension provides a few **expressions and conditions** to help you monitor saves and loads. In particular, the "Load just succeeded" condition is perfect to run some logic after a scene was loaded. This is somewhat similar to "At the beginning of the scene", except that after a loading a scene is already considered as started (because it was "frozen in time" in the save state). +The extension provides **conditions and expressions** to help you monitor saves and loads: + +- **Save just succeeded** / **Save just failed** — trigger on the frame immediately after a save attempt completes. Use these to show a "Game saved!" message or handle a storage-full error. +- **Load just succeeded** / **Load just failed** — trigger on the frame after a load attempt. "Load just succeeded" is perfect for running initialization logic after a scene is restored, similar to "At the beginning of the scene" — except the scene is already considered started (it was "frozen in time" in the save state). +- `SaveState::TimeSinceLastSave()` — returns the number of seconds elapsed since the last successful save, or `-1` if no save has occurred yet in this session. Useful for implementing autosave timers. +- `SaveState::TimeSinceLastLoad()` — returns the number of seconds elapsed since the last successful load, or `-1` if no load has occurred. ## Advanced: Excluding Objects from Save States with the “Save Configuration” Behavior.