Skip to content
CISV2.0GET IT ON FAB

MULTIPLAYERMULTIPLAYER

Multiplayer

A use runs on more than one machine. The player’s machine starts it at once, the server checks it, and the other machines show it. This page describes how the plugin does that in a game with more than one player. It describes the 2.0 behaviour.

  • It runs each Action and MoveHandler on the machines that its Net Execution Policy names.
  • It makes a copy of an object for each use on each machine, when the object’s Instancing Policy says so.
  • It lets the server accept or refuse each use request of a remote player, with the tolerances of the project’s network settings.
  • It lets the player’s machine start a use before the server answers, and takes the predicted use back when the server refuses it.
  • It gives the object’s actor to the player for the length of a use, and can let the actor rest while nobody uses it.
  • It sends the object’s state to every machine, and keeps the data object of a character only on the machine of the player who controls that character.

Machines and steps. A use has two steps, the movement of the MoveHandler and the Action. Each step has one leader, and the other machines follow the leader’s phases. The machine that leads the Action also ends the use, and the other machines end with it. An interruption, such as a rejection by the server, ends the use on every machine. The Net Execution Policy of each Action and MoveHandler names the machines it runs on:

  • All Machines (ECISNetExecutionPolicy::AllMachines), the default: on every machine. The player’s machine leads the step.
  • Local Predicted (LocalPredicted): on the player’s machine and on the server. Whichever of the two completes the step first leads it, and the other follows.
  • Server Only (ServerOnly): on the server only. The server leads the step, and no client runs it.
  • Local Only (LocalOnly): on the player’s machine only. The player’s machine leads the step.

A step with no object, such as a side without a MoveHandler, is led by the player’s machine. In a standalone game, and for a listen host’s own character, one machine runs and leads the whole use. On the server, the run of a remote player’s use follows that player’s steps. It leads the steps whose objects are Server Only, and the Local Predicted steps together with the player.

Instancing. The Instancing Policy of an Action or MoveHandler, ECISInstancingPolicy, decides whether one object serves every use:

  • Default is chosen by the class: SharedPerObject for a C++ class, and InstancedPerExecution for a Blueprint class.
  • SharedPerObject: the object that is set on the interaction object runs every use, and keeps its variables between uses.
  • InstancedPerExecution: each machine that runs a use runs a new copy of the object, and the copy is destroyed when the use ends.

A class with its own RPCs or replicated properties always runs as SharedPerObject.

The check on the player’s machine. Before it sends anything, the player’s machine checks the object as the character’s data object does. The object must be visible to the character: in reach, facing the character, usable, not used by anyone else, the character not using another object, and every Visible Condition must pass. Then every Usable Condition must pass. A use that fails here sends no request and writes no log line. Otherwise the player’s machine sends ServerRequestUse with the use’s prediction key, starts the use at once, and waits for the answer. The request goes out before the use starts, so the server handles it before anything else this use sends for the object’s actor.

The check on the server. The server checks only the requests of remote players. A standalone game, a listen host’s own character and the characters the server runs are not checked. For a remote player’s request, the server first rejects a use that it already runs for that character, then a side or an Action index that its copy of the object does not have. Then it runs ValidateUseRequest, which takes the first failing check of this order:

  1. ObjectInvalid: the object or its actor is missing or being destroyed, or the character is not valid.
  2. NotUsable: the object’s usable flag is off.
  3. Occupied: another character uses the object.
  4. NoActionForState: no Action of the side matches the object’s state.
  5. BadSide: the character does not stand within the requested side.
  6. OutOfRange: the character stands farther from the object than its Interaction Distance, plus the tolerances.
  7. BadAngle: the character does not face the object, within the angle plus the tolerance.
  8. ConditionFailed: a Condition that the server validates fails. See Conditions.
  9. HoldTooShort: the hold was released before the object’s use time, less the Hold Time Tolerance, or the server saw no hold for an object with a use time.

The server then answers the request. A rejection tells the player’s machine the reason, and the object’s state and usable flag on the server. The predicted use ends as interrupted, with WasRejectedByServer set, and the player’s copy of the object takes the server’s state and usable flag. The reasons are the values of ECISUseRejectReason. The table in Troubleshooting says what to check for each.

The server writes a line for a refusal in the log category CISLog, at the Log level: ServerRequestUse from <character>: the use of <object> is rejected (ECISUseRejectReason::<reason>), key <key>.. The category shows only warnings by default. Type log CISLog Log in the console to see them. No such line appears when the server cannot find the object at all, or when the server runs its own use of that character. The player’s machine writes ClientRejectUse on <character>: the server rejected use <key> (ECISUseRejectReason::<reason>). only while its use still waits for the answer. The walkthrough Object for multiplayer shows both lines in a test with two players.

Prediction. A predicted use runs on the player’s machine before the server answers. An Action or MoveHandler with All Machines or Local Predicted runs there, and one with Server Only does not. IsLocallyPredicted is true on a client that runs the Action or the MoveHandler for its own player’s use, including the Local Only ones. It is false on the server, on a listen host’s own character and in a standalone game. WasRejectedByServer is true when the server refused the use. A predicted use that the server refuses ends as interrupted, and the player’s copy of the object takes the server’s state and usable flag.

Ownership during a use. With Transfer Ownership During Use on, which is the default, the user’s controller becomes the owner of the object’s actor for the length of the use. The server sets and returns the owner. When the use ends, the actor’s previous owner comes back.

Dormancy. Manage Owner Dormancy is off by default. When it is on, the object’s actor rests dormant from begin play, unless the actor sets its own dormancy, and a use wakes it. The actor rests again 1 second after the use ends. The option works only on an actor that replicates, on the machine with authority, and not in a standalone game. The object’s own replicated state, its usable flag and its in-use flag still reach every client. Turn it on only for an object that replicates no variables, multicasts or other network functions of its own, and whose dormancy no other code sets. The walkthrough Object for multiplayer, step 6, has the same rule.

State changes. SetObjectState changes the state on the machine it runs on, and it belongs on the server. On the authority, the component fires the change only when the new state differs from the current one. OnObjectStateChangedDelegate runs on every machine, and on a client it runs when the replicated state arrives. The change that a finished Action makes with New State After Finish Action is the plugin’s own, so it makes no warning.

On a client’s copy of an object, a simulated proxy, SetObjectState changes only that copy, and the Output Log warns once: ... called on a client; the change is local only. Call it on the server. A client’s copy stays a simulated proxy while its controller owns the actor during a use. Only an autonomous proxy, a pawn the client controls, also sends the call to the server. SetUsable follows the same rule. Code that changes the state runs on the server, behind an Is Server branch. See Interaction object component.

Where data objects live. The character’s interaction component starts only on the machine of the player who controls the character. The data objects of the character are created there, so the server and the other players have none for this character. Their events, such as OnObjectUseStarted, run only on that machine, and so does the data object’s Tick, which sets the visible and usable flags. A hint or an icon drawn by a data object is seen only by that player. See Data objects.

The steps are in the guides, so this page gives only the outline. Follow these guides in order:

  1. Turn on Replicates on the object’s actor, in Class Defaults, as Object for multiplayer does in its step 1.
  2. Check the project’s network settings, as its step 2 does.
  3. Choose the Net Execution Policy of each Action and MoveHandler, as its step 3 does.
  4. Choose the Server Validation of each Condition, as its step 4 does.
  5. Change the state on the server only, behind an Is Server branch, as its step 5 does.
  6. Check Transfer Ownership During Use and Manage Owner Dormancy on the interaction object, as its step 6 does.
  7. Test with two players, as its step 7 does.
  • Choose the Net Execution Policy by who must see the object. All Machines suits an effect that everyone sees, Server Only suits what must happen once, and Local Only suits what only the player should see.
  • Read the role of the machine in an Action or MoveHandler with IsExecutingOnServer, IsLocallyPredicted, IsSimulatedExecution and WasRejectedByServer. GetExecutionContext returns the whole context.
  • Change the object’s state on the server, behind an Is Server branch, and bind On Object State Changed Delegate for what every machine must show.
  • Put what only the player should see in the data object’s events, and what every player must see in the object’s own events.

The settings of UCISNetworkSettings are the project’s network settings. They are saved in the project’s DefaultGame.ini, and the server reads them for every use request it checks.

Server Validation
Server Validation
Distance Tolerance Absolute100.0
Distance Tolerance Scale0.5
Angle Tolerance Degrees30.0
Hold Time Tolerance0.3
The Server Validation settings of the Character Interaction System project settings, with their default values.
SettingWhat it doesDefault
Server ValidationWhether the server checks every use request of a remote player and rejects a request that fails. Off: the server confirms every request, and still refuses a side or an Action it does not have, and a use that is already running.On
Distance Tolerance AbsoluteCentimetres the character may stand beyond the object’s Interaction Distance.100
Distance Tolerance ScaleFraction of the object’s Interaction Distance the character may stand beyond it. 0.5 means half the distance more.0.5
Angle Tolerance DegreesDegrees added to the half angle of the requested side, and to the half facing angle of the character.30
Hold Time ToleranceSeconds by which a hold measured on the server may be shorter than the object’s use time.0.3
  • With the two players of Object for multiplayer, step 7, both players hear the sound, and the state changes on both machines when one of them presses E at the object.
  • With the refusal Condition of that walkthrough in place, the first player’s use is refused. The state stays as it was on every machine, and the Output Log shows the server’s line with ECISUseRejectReason::ConditionFailed.
  • A use that the player’s machine blocks itself, because the character is too far from the object or faces it at the wrong angle, writes no line on any machine.
  • Other players do not see the object change. The object’s actor does not replicate. Turn on Replicates in Class Defaults. See Object for multiplayer.
  • A use works alone and is refused online. The server checks the request, and the reason is in the Output Log. The reasons and what to check for each are in Troubleshooting, “Works alone, fails online”.
  • A refusal shows no server line. The server cannot find the object at all, or it runs its own use of that character. The player may then see the state snap back with no line on the server.
  • The Output Log warns that a change is local only. A client called Set Object State on its copy of the object. Put the call on the server. See Interaction object component.
  • Only one player hears the Action. Its Net Execution Policy is not All Machines. See Actions.