23. Co-op and online
In short: RPG Saga Engine lets two to four players play one game together: one player hosts, friends find or are invited to the session, and everyone explores, talks and fights in the host's world. You do not write networking code. Your own systems go online by declaring their state and their actions, the same way they already do for saving.
23.1 Basics
23.1.1 The two layers
Co-op is built from two layers that you can use separately.
| Layer | Module | What it does |
|---|---|---|
| Online sessions | RSEOnline | Getting players into the same game: host, find, join, invite, leave, reconnect, clear error messages, a lobby screen. Works on a local network (LAN), on Steam and on Epic Online Services (EOS) behind one interface. |
| Co-op | RSENet | What the players share once they are in: the party, the inventory, the story, quests, the map, the clock and live battles. Every player drives a party member. |
Both are optional. They load with the plugin and switch on only when the game runs a network session. Single player never pays for them, and it never notices the co-op rules. You can turn the whole thing off with the Online and Co-op feature (Project Settings → RPG Saga Engine → Features).
23.1.2 The model in one minute
- The host is the authority. The host's machine runs the game logic: encounters, NPCs, quests, the clock. Guests show the host's state and send their actions to the host.
- Actions are commands. A player's action goes through the command bus (Submit Command). In single player the bus applies it at once. On a guest it travels to the host, which checks it, applies it and sends the result back. One code path serves every case.
- State is replicated by section. The state your systems already save (party, story, quests, world, map, time) is also what the host sends to guests. Each save section is sent, compressed, when it changes. A player who joins late receives everything once.
- Players drive party members. A joining player takes a party member from the companion AI, the character chosen in the lobby if it is free. When a player leaves, the companion AI takes the member back at once. A player who comes back gets the same slot and, if it is still free, the same member.
- World actors (explorers, companions, roaming enemies, NPCs) are ordinary replicated Unreal actors. Their logic runs on the host only.
The maximum is four players, host included. The session is a listen server: the host plays too, and no dedicated server is needed.
23.1.3 Try it on a local network in two minutes
LAN needs no account and no setup.
- Start the game twice as standalone games (Play → Standalone Game, twice), or on two PCs on the same network. Use a map with an exploration game mode. The plugin's example map works.
- In the first window open the console and type
RSE.Online.Host. The log showshost listening on ... ok. - In the second window type
RSE.Online.AutoJoin. It searches until it finds the host and joins. - The guest appears as a party member that the host's companion AI was driving. Walk around in both windows.
RSE.Online.Statuslists the session and the players.RSE.Net.Statuslists the players, their members and positions, the state sections and the traffic.RSE.Net.Stats 10measures the bandwidth for ten seconds.
For a screen instead of commands, RSE.Online.Lobby opens the built-in lobby: Host, Find, a list of sessions to join, the players and your character choice. With the default settings the main menu also offers Host Game and Join Game, and the in-game menu offers Host co-op game, Invite friends, Join a game and Leave co-op game.
23.1.4 The co-op rules you get for free
The group plays the host's story together. These rules are built in. Each has settings and a replaceable policy class, so you change a rule without touching the framework.
- Dialogs. The one who starts a dialog speaks and everyone watches it. Story choices are made by the host, by the speaker or by a vote (setting in the dialog settings, or the choice node's Decider). The dialog box shows who speaks and who decides.
- Story scenes play on every machine. Guests' input is locked, and only the host can skip (or a vote, or anyone, by the Scene Skip Rule).
- The leash. Guests may roam up to Leash Radius from the host (or from the center of the group). Past it they get a warning, and after Leash Grace Seconds they are brought back beside the group. It is off during scenes, dialogs, battles and travels. Place an RSE Co-op Map Settings actor in a map to change the leash there.
- Zone travel. The host's travel starts a short countdown on every screen and takes everyone along. A guest at an exit asks the host, who answers a question box.
- Notices. Refused actions show their reason to the player who tried them. Everyone sees "the host is saving", "Player 2 joined", "the companion AI takes over".
- Battles are live. While a session has more than one player, grid Tactics and ATB encounters are fought in Free Tactics on the map where the party stands. Each player commands the unit of the member they drive. Real-time Action encounters stay as they are. See Free Tactics (Live) and Action battles.
- Companions and equipment. Every player may order companions the AI drives, never a member another player drives. A guest changes only the equipment and Pacts of the member they play. Both are settings.
23.2 How it works
23.2.1 Sessions
A session goes through a small state machine owned by the session subsystem:
host: Idle -> Creating -> Hosting
search: Idle -> Searching -> Idle
client: Idle -> Joining -> Connected
drop: Connected -> Reconnecting -> Joining -> Connected
end: Leaving -> Idle
Only one operation runs at a time. A second one is refused with Busy rather than mixed up with the first. Every step has a timeout (Operation Timeout Seconds), and every failure has its own player-facing message: the service is unavailable, you are not signed in, the session is full, the host runs another version, the host refused new players during a battle, the connection was lost, and so on.
The online service is a provider: LAN, Steam, EOS, or a class of your own. The session flow, the version check, the reconnecting and the lobby are the same for all of them.
23.2.2 Commands
Every player action is a command: a small serializable struct, a validator and an applier.
- A widget or a Blueprint submits the command (Submit Command).
- On the host or in single player the bus calls the handler's Validate, then Apply, at once.
- On a guest the bus sends the command to the host. The host fills in the sender from the connection, never from the packet, so a client cannot pretend to be someone else. It validates, applies and returns the result.
- The system's own events fire as usual, and the state section it changed replicates.
Commands refer to content by id (tags, primary asset ids, soft references), never by pointer. Commands that need randomness take their seed from the game's command stream, not from the global random generator, so the host's result is reproducible.
23.2.3 State sections
A state section is one named block of serializable state, the same block the save system writes. The owner marks it changed when its state changes. The network layer then sends it when the revision grew and the bytes differ from what was last sent. A section has a scope:
| Scope | Sent to |
|---|---|
Shared (default) | everyone |
PerPlayer | its owner only (tutorial progress, for example) |
HostOnly | nobody (difficulty, seeds) |
A change you forget to mark is still caught by a safety net, the change detection, a little later and at some cost. Mark your changes anyway.
23.2.4 Live battles
The host's machine runs the battle. Every machine shows it with its own camera, HUD and menus. The host streams the battle's numbered events and state to each guest, and a guest that falls behind catches up at a higher playback rate. A battle's start snapshot travels in confirmed chunks. When another player's unit acts, your HUD says who is choosing, then shows the action. After Turn Timeout Seconds the host may command an unanswered unit for one turn (default), or the AI plays it. A player who leaves mid-battle is replaced by the AI at once, and a player who joins watches until the battle ends.
Real-time Action battles use their own stream for co-op: the host is authoritative for damage, the guest moves its own character with client prediction, plays its swing at once and claims its hits through the command bus. Its transport budgets are in Co-op Network → Action Battle.
23.2.5 Zone travel
The host's travel is a server travel. Guests reconnect to the new map and keep their slots, behind a short loading screen. Seamless travel is not used. A guest cannot start a travel on its own.
23.3 Key classes and assets
| Class / asset | Module | Role |
|---|---|---|
URSESessionSubsystem | RSEOnline | The one object for the session flow: host, find, join, leave, invites, lobby, state and errors. Blueprint and C++. |
URSESessionProvider | RSEOnline | Base of an online service. URSESessionProvider_OSS works with any Online Subsystem. URSESessionProvider_LAN, _Steam and _EOS derive from it. |
URSEOnlineSettings | RSEOnline | Project Settings → RPG Saga Engine → Online. |
URSELobbyWidget, URSELobbyViewModel | RSEOnline | The built-in lobby screen and the data a replacement screen reads. |
URSEOnlineMenuEntry | RSEOnline | Adds Host Game, Join Game and the in-game entries (a URSEMenuExtension). |
URSENetSettings | RSENet | Project Settings → RPG Saga Engine → Co-op Network. |
ARSENetGameMode, ARSENetGameState, ARSENetPlayerState | RSENet | The exploration game mode with the co-op components built in. Use it for maps meant for co-op. |
URSENetSubsystem, URSENetWorldSubsystem | RSENet | The transport: commands up, results and events down. URSENetWorldSubsystem::SendToPlayer, SendToGuests and RegisterEventHandler carry your own messages. |
URSENetPlayerComponent | RSENet | On each player state: the slot (GetPlayerId) and the player's own state sections. |
URSENetBattleSubsystem, URSENetActionBattleSubsystem | RSENet | The live-battle and Action-battle streams. |
URSECommandSubsystem, FRSECommand, URSECommandHandler | RSECore | The command bus and its building blocks. |
URSESaveSubsystem, RSEStateSections::MarkChanged | RSECore | The state sections (the same ones the save system writes). |
URSEPlayers, URSEPlayerProvider, URSEAuthorityPolicy | RSE | Who the players are (GetControllerFor, GetPawnFor) and when the co-op rules apply (HasAuthority, IsMultiplayerSession). |
URSECoopNoticeSubsystem | RSE | One notice path for every player. |
URSECoopRulesSettings, URSECoopFlowSettings | RSECore / RSE | The co-op rules and the flow settings (leash, travel, scene skip, texts). |
ARSECoopMapSettings | RSE | A per-map actor for the leash radius and the guests' start spacing. |
There are no data assets to create for co-op. Your first work is in settings.
23.4 Settings
23.4.1 Online
Project Settings → RPG Saga Engine → Online (URSEOnlineSettings):
| Setting | What it does | Default |
|---|---|---|
| Provider Class | The service: LAN (Null subsystem), Steam, Epic Online Services, or your own provider class. | LAN |
| Fallback To LAN | When the chosen service is not available (Steam not running, EOS not configured), use LAN instead of failing. | on |
| Game Version | Players join only hosts with the same version. Empty uses the project version. | empty |
| Max Players | Players in a session, host included (2 to 4). | 4 |
| Visibility | Public, Friends Only or Invite Only. LAN sessions are always visible on the local network. | Friends Only |
| LAN Port | The game port the host listens on. | 7777 |
| Host Map | The map the host opens when hosting. Empty stays on the current map and opens it to players (drop-in). | empty |
| Join In Progress Policy | A player who joins during a battle: spectate until it ends, join immediately, or be refused. | Spectate |
| Keep Slots For Reconnect | A player who drops keeps the slot and character until a new player needs the place. | on |
| Operation Timeout Seconds | Any step (create, search, join, travel, leave) that takes longer fails with a clear message. | 30 s |
| Auto Reconnect, Reconnect Attempts, Reconnect Delay Seconds | A client whose connection drops rejoins the same session. | on, 3, 3 s |
| Lobby Widget Class, Lobby Z Order, Lobby Panel Width | The lobby screen. | built-in |
For one run the command line -RSESessionProvider=LAN (or Steam, EOS, or a class path) overrides the provider.
23.4.2 Co-op network
Project Settings → RPG Saga Engine → Co-op Network (URSENetSettings):
| Setting | What it does | Default |
|---|---|---|
| Enable Coop | Installs the co-op layer whenever the game runs a network session. Off means your project replicates on its own. | on |
| Section Send Interval | How often the host looks for changed state sections. | 0.1 s |
| Change Detection Interval | How often the host compares every section's bytes to catch unmarked changes. 0 = never. | 0.5 s |
| Min Section Resend Interval | A section that keeps changing is sent at most this often. The latest state always goes out. | 0.2 s |
| Compress Sections, Compression Min Bytes, Compression Format | Sections above the minimum are compressed. | on, 256, Oodle |
| Max Section Bytes | A larger section is not replicated and is reported as an error. Split that state. | 256 KB |
| Max Command Bytes | Larger commands are refused. A much larger packet disconnects the sender. | 4096 |
| Max Commands Per Second | Per guest. More are refused, not applied. | 30 |
| Max Queued Commands | Commands a guest keeps while it is still connecting. | 32 |
| Broadcast Result Types | Command types whose results every player receives, for notices. | none |
| Player Provider Class, Authority Policy Class | Who the players are, and when co-op rules apply (from the second player). Replaceable. | built-in |
| Assign Member On Join | A joining player takes a party member from the companion AI. Off means you assign members yourself. | on |
| Class Relevancy | Per actor class: cull distance, update rate and priority. | built-in |
The Battle and Action Battle categories hold the stream tuning (intervals, chunk sizes, the Action mirror's byte budget, claim limits). The defaults are measured. Leave them alone until you have a reason to change them.
23.4.3 Rules and flow
| Where | Main values |
|---|---|
Co-op Rules (URSECoopRulesSettings) | Default Encounter Conversion (Free Tactics), Companion Orders (Any Player), Guest Equipment (Own Character), the policy classes |
Co-op Flow (URSECoopFlowSettings) | Leash on, Host anchor, 40 m, 4 s grace; Scene Skip Rule (Host Only); Group Travel Countdown (3 s); every notice text |
| Free Tactics (Co-op) | Companion Units (AI), Turn Timeout Seconds (45), Turn Timeout Action (Host Takes Over), Pull In Far Players (on), Reconnect Retakes Unit At Once (off) |
Per encounter, the Co-op Conversion field chooses what happens in a session: fight it live, use a Co-op Alternative made for co-op, or keep its mode for a set piece. The encounter's validation warns when something would not convert.
See Appendix B for every property.
23.5 Blueprint usage
23.5.1 Host, find and join
All session nodes live on the RSE Session Subsystem (Get Game Instance → Get Subsystem (RSE Session Subsystem)) and in three latent nodes.
Host from a menu button:
- On the button's On Clicked, call Host Session (Async). Leave Params at their defaults to use the project settings, or fill the optional fields to override max players, visibility, label or map.
- On Success: hide the menu. The game is now hosting and other players can find it.
- On Failure: pass the Error to Get Session Error Message and show the text in a message box.
Find and join:
- Call Find Sessions (Async). On Success gives the results. Each has a label, map, players, ping and whether it can be joined (Is Session Joinable tells you).
- Show them in a list. When the player picks one, call Join Session (Async) with its Result Index.
- On Success fires once the client is connected and the map has loaded.
Other nodes on the subsystem: Leave Session (also cancels a search or a reconnect), Show Invite UI, Query Friends, Invite Friend, Set Preferred Character, Open Lobby Screen, Set Battle In Progress, Is In Session, Is Host, Is Multiplayer, Get Session State, Get Last Error. Events to bind: On State Changed, On Session Error, On Sessions Found, On Lobby Changed, On Player Joined, On Player Left, On Player Admission Changed, On Friends Received, On Invite Accepted.
23.5.2 Submit a command from a widget
- Pick the command struct. New command structs are declared in C++ (see below). The framework's own commands, such as the party and quest commands, are ready to use.
- In the widget: Get Game Instance → Get Subsystem (RSE Command Subsystem).
- Make Instanced Struct with the command struct and its fields.
- Submit Command. The returned result says Applied, Rejected or Forwarded (a guest sent it to the host).
- Bind On Command Result to show the final outcome. On a guest it arrives later, after the host answered.
23.5.3 A handler in Blueprint
- Create a Blueprint class from RSE Command Handler.
- Set Command Type to the command struct. Set Permission (Any Player or Host Only) and, for host-only commands, a Denied Reason. Untick Uses Seed if the command has no randomness.
- Override Validate (return false with a reason the player can read) and Apply (change the state).
- List the class in Project Settings → RPG Saga Engine → Commands → Handler Classes.
23.5.4 Notices
Call Notify (one player) or Notify All on the RSE Co-op Notice Subsystem. Bind On Notice to draw the notice in your own widget.
23.5.5 Authority checks
Use Has Game Authority (or Has Authority from the players library) before logic that must run once (spawning, rewards, timers). It is true in single player and on the host, false on a guest. Is Multiplayer Session tells you whether the co-op rules apply.
23.6 C++ usage
23.6.1 Making your system's state replicate
If your system already saves through the save system, it is already replicated. Register the section, set its scope, and mark it when it changes:
// In your subsystem's Initialize (the same registration as for saving):
URSESaveSubsystem* Save = Collection.InitializeDependency<URSESaveSubsystem>();
auto Section = Save->RegisterStruct<FMyReputationState>(TEXT("Reputation"), 1, this, [this]() { return &State; });
Section->Scope = ERSEStateScope::Shared; // Shared, PerPlayer or HostOnly
// Whenever the state changes (on the authority):
RSEStateSections::MarkChanged(this, TEXT("Reputation"));
On a guest the section arrives and is applied through the same code as a load. Listen to URSESaveSubsystem::OnSectionChanged (Blueprint: On State Section Changed) to refresh the screen.
23.6.2 Making your system's actions commands
USTRUCT()
struct FMyCmd_Praise : public FRSECommand
{
GENERATED_BODY()
UPROPERTY() FPrimaryAssetId Town;
};
// In Initialize:
URSECommandSubsystem* Commands = Collection.InitializeDependency<URSECommandSubsystem>();
Commands->RegisterHandler<FMyCmd_Praise>(
[this](const FRSECommandContext& Context, const FMyCmd_Praise& Cmd, FText& OutReason)
{
return Cmd.Town.IsValid(); // never change state here
},
[this](const FRSECommandContext& Context, const FMyCmd_Praise& Cmd)
{
State.Add(Cmd.Town, 1); // Context.Instigator is the sender
RSEStateSections::MarkChanged(this, TEXT("Reputation"));
},
ERSECommandPermission::AnyPlayer, this);
// From a widget or gameplay code:
Commands->Submit(FMyCmd_Praise{ TownId });
Context.Instigator is the player id of the sender, set by the host's transport. For a public function that must keep its signature in single player, RSECommands::SubmitOrApply wraps the pattern: single player applies at once, a guest sends the command.
23.6.3 Messages from the host to a player
Any USTRUCT can travel from the host to one player or to all guests:
// Inside any class that has a world:
URSENetWorldSubsystem* NetWorld = GetWorld()->GetSubsystem<URSENetWorldSubsystem>();
NetWorld->RegisterEventHandler(FMyEvent_Praised::StaticStruct(), this,
[this](const FInstancedStruct& E) { ShowToast(E.Get<FMyEvent_Praised>()); });
NetWorld->SendToPlayer(Context.Instigator, FInstancedStruct::Make(FMyEvent_Praised{ /*...*/ }));
// or NetWorld->SendToGuests(...)
The framework's own screens work this way: screens opened by a player's action (shop, code lock, lockpick), puzzle results and dialog votes.
23.6.4 Your own actors
Actors in the world follow the usual Unreal rules: replicated properties and OnRep functions. Put the game logic behind URSEPlayers::HasAuthority(this) so it runs on the host only. Find a player from a command with URSEPlayers::GetControllerFor(PlayerId) and GetPawnFor(PlayerId).
Add RSENet to your module's dependencies only if you use its types directly. Systems that stay on the bus and the state sections need no dependency on it.
23.6.5 Extension points
| Subclass | To change |
|---|---|
URSESessionProvider (or URSESessionProvider_OSS) | Add another online service. Implement Host, Find, Join and Leave, answer each request with its Report... call, and pick the class in Provider Class. A Blueprint child of the OSS provider with another Subsystem Name is often enough for a new platform. |
URSEPlayerProvider, URSEAuthorityPolicy | Who the players are, and when co-op rules apply. |
URSELeashPolicy, URSEGroupTravelPolicy, URSECoopEncounterPolicy | The leash, group travel and encounter conversion rules. |
URSEMenuExtension | Your own entries in the main and in-game menus. |
URSELobbyViewModel / URSELobbyWidget | A custom lobby screen. Derive a Widget Blueprint from RSE Screen Widget, read the view model and call its actions (Host, Find, Join, Leave, Invite, Cycle Character). Set it as Lobby Widget Class. |
23.7 Advanced
23.7.1 Steam
- Enable the engine plugin Online Subsystem Steam, then restart the editor.
- Add to
Config/DefaultEngine.ini:
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="/Script/SocketSubsystemSteamIP.SteamNetDriver",DriverClassNameFallback="/Script/OnlineSubsystemUtils.IpNetDriver")
[OnlineSubsystem]
DefaultPlatformService=Steam
[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480
bInitServerOnClient=true
[/Script/SocketSubsystemSteamIP.SteamNetDriver]
NetConnectionClassName="/Script/SocketSubsystemSteamIP.SteamNetConnection"
- Set Online → Provider Class to Steam.
- Test with two PCs and two Steam accounts. Steam does not work inside the editor viewport: package the game or run it standalone.
App ID 480 is Valve's public test app. It is fine for development, but every developer using it sees the others' sessions (RPG Saga Engine lists sessions of other games and versions as incompatible and does not let you join them). Use your own App ID before release. If Steam is not running, the game falls back to LAN and the log says why.
23.7.2 Epic Online Services
- Create a product in the Epic Developer Portal: a product, a sandbox, a deployment, and a client policy that allows peer-to-peer and lobbies. Write down the Product, Sandbox, Deployment and Client IDs and the Client Secret.
- Enable the engine plugin Online Subsystem EOS.
- In Project Settings → Plugins → Epic Online Services add an artifact with those IDs and a 64-character hexadecimal encryption key. Tick Use EAS and/or Use EOS Connect.
- Add to
Config/DefaultEngine.ini:
[OnlineSubsystem]
DefaultPlatformService=EOS
[OnlineSubsystemEOS]
bEnabled=true
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="/Script/SocketSubsystemEOS.NetDriverEOS",DriverClassNameFallback="/Script/OnlineSubsystemUtils.IpNetDriver")
[/Script/SocketSubsystemEOS.NetDriverEOS]
bIsUsingP2PSockets=true
- Set Provider Class to Epic Online Services.
- Players must be signed in. For development, run the EOS Developer Authentication Tool and start each game with
-AUTH_TYPE=developer -AUTH_LOGIN=localhost:<port> -AUTH_PASSWORD=<credential name>. The provider signs in automatically before hosting or searching and reports Not signed in when it cannot.
23.7.3 What was tested
Be honest with your players and your testers about this. The two-process LAN test (Tools/Dev/coop_lan_smoke.ps1 in the repository) covers join, a guest command, both players moving, a state change, leaving, zone travel, dialogs and scenes, and live battles including a player leaving mid-battle. Steam and EOS have been exercised on a local network only, not between distant machines. Four-player sessions under bad network conditions and hosting on a Steam Deck are not yet tested. Competitive PvP is not part of this release.
23.7.4 Bandwidth
Measured on the example map, two players, LAN:
| What | Measured | Budget |
|---|---|---|
| Joining (every state section, once) | about 5 KB sent | 60 KB |
| Exploring, per connection | 4 to 6 KB/s average | 15 KB/s average, 40 KB/s peak |
| A guest command | about 0.3 KB | |
| A live battle, the battle stream | about 0.9 KB/s | 5 KB/s |
If your own state is large, keep sections small and split them (Max Section Bytes reports offenders in the log).
23.7.5 Test without a second machine
RSE.Net.SimulateMultiplayer 1makes the default authority policy report a multiplayer session, so the co-op time and pause rules apply.RSE.Net.ForceProxy 1makes it report no authority, as on a client: logic timers stop. Both are for debugging and tests.RSE.Net.LogTraffic 1logs every section and command.RSE.Net.Dev.Walk X Y Seconds,RSE.Net.Dev.SwitchLeader,RSE.Net.Dev.Kick Slot(host),RSE.Net.Dev.ScriptWhen guest|synced|noguest ScriptFiledrive a test without clicks.RSE.Coop.Dev.Prompt [Host|Speaker|Vote],RSE.Coop.Dev.Advance,RSE.Coop.Dev.Choose <Option>,RSE.Coop.Dev.StatusandRSE.Coop.Dev.Notify <Text>test the co-op dialog, leash and notices.- Start two processes with
-ExecCmds="RSE.Online.Host"and-ExecCmds="RSE.Online.AutoJoin 60"(add-RSESessionProvider=LAN -nosteam). A label (RSE.Online.Host [Max] [Label],RSE.Online.AutoJoin [Seconds] [Label]) keeps test sessions apart.
All console commands are listed in Appendix C.
23.7.6 Rules for network-ready systems
Whenever you add a system, follow the same rules the framework follows:
- Actions go through commands. UI and input do not change shared state directly. They submit a command.
- One owner per piece of state. Others read it and listen to its events. Do not keep private copies that can drift.
- Presentation feeds on events. A client that only receives the events must be able to show the same thing.
- Control of a character is swappable. The local player, the AI or a remote player take the same path. Never assume one local player: avoid
GetPlayerController(0)in logic. - Determinism. Use
URSESeedSource::NextSeedFor(this, Stream)or the seed in the command context, never the global random functions, for anything that affects the game. - Guard effects. Logic with effects in the game runs behind
URSEPlayers::HasAuthority(orURSEAuthoritySubsystem::HasAuthorityFor). - No RPCs in framework-style code. A system goes online through its save section and its commands. Actors of your own can use ordinary replication.
23.8 Troubleshooting
| Symptom | Cause and fix |
|---|---|
| The online service is not available | The provider's subsystem is not loaded: the engine plugin is not enabled, DefaultPlatformService is not set, Steam is not running, or the game was started with -nosteam. The log line RSEOnline: ... is not available (...) names the reason. With Fallback To LAN on, the game uses LAN instead. |
| LAN finds nothing | Both games must be on the same subnet. Allow the game through the firewall: UDP for the LAN beacon port (14001) and the game port (7777 by default). |
| The host is running a different version | Set the same Game Version (or project version) on both builds. |
| Steam: sessions of other games appear | App ID 480 is shared. They are listed as incompatible and cannot be joined. Use your own App ID. |
| EOS: Not signed in | Start the game with the developer authentication arguments, or sign in through your own login flow before hosting. |
| Your system works for the host but not for guests | Its state is not a state section, or the owner never calls MarkChanged. Check RSE.Net.Status for the section, and read the log for Max Section Bytes errors. |
| A guest's action does nothing | It is not a command, or the handler's Validate refused it. Turn on Commands → Log Refused Commands (or RSE.Commands.Log 1) and read the reason. |
| A guest is refused with a rate message | More than Max Commands Per Second arrived. Lower the rate of the sender, or raise the setting. |
| Timers and spawns run on guests too | The logic is not behind URSEPlayers::HasAuthority. |
| A guest keeps being pulled back | The leash. Raise Leash Radius, change the anchor, or place an RSE Co-op Map Settings actor. |
23.9 See also
- Save and load: the same sections are what is replicated.
- Free Tactics (Live) and Action battles: co-op battles.
- Extending the plugin: modules, extension points and the engineering rules.
- Platforms and performance: the Steam Deck and the save backends.
- Product pages:
docs/product/coop.md,docs/product/online-sessions.md, and the design recorddocs/design/networking.md.