Skip to main content

Appendix C - In-Game Communication

Multiplayer Architecture

We should use one of the below methods to handle multiplayer functionality. Corresponding GameArchType should be returned in the GetGameArchType overridden function.

Generic

We go for Generic architecture if the game has its own multiplayer network (like photon) to handle multiplayer in the game or if the game is a single player game. Here GetGameArchType overridden function should return GameArchType.Generic


RemoteEventBased

If the game requires simple event transfer between remote players, event loss during disconnection is acceptable and doesn’t need master client(it is a client like normal client along with some extra functionality and decision making capability), then the following steps need to be done for event transfer (PUN2 is used for events transfer)

  • In the file created in section 2 above

    • Override public virtual GameArchType GetGameArchType() to return GameArchType.RemoteEventBased
* Override `public virtual void onGamePlayEvent(object data) to listen to the event data fired by each player in section 4.1.c.ii.`

API Calls

  • API present in IRemoteEventBasedGameManager should be called from appropriate places in the game. An Object implementing IRemoteEventBasedGameManager can be obtained by calling JoyrideGame.instance.getRemoteEventBasedGameManager().
  • API present in IRemoteEventBasedGameManager are:

    • bool isConnectedToServerWithProperState(); //it returns true only if startGamePlay Command received and current user is connected to the game room on server

    • void sendGamePlayDataToRemotePlayers(object data, bool sendToMeAlso = true); //It sends data to remote players via game server. I also sends data to the local player if sendToMeAlso is true


GenericRemote

If the game requires events transfer along with the master client(it is a client like a normal client along with some extra functionality and decision making capability), then following steps need to be done to use our framework for event transfer (PUN2 is used for events transfer. The framework handles master client switching, disconnection, automatic event generation during disconnection, event generation for GameAI etc)

  • In the file created in section 2 above,
    • Override public virtual GameArchType GetGameArchType() to return GameArchType.GenericRemote
    • Override public virtual INonGameSpecificCommandHandler createGenericGameManager(GameConfig gameConfig) to create an instance of GenericGameManager (GenericGameManager class implements IGenericGameManager and INonGameSpecificCommandHandler) and store reference of IGenericGameManager.
  • Constructor of GenericGameManager class takes following 3 argument
    • IGameEventListener<S, PS, A> gameEventListener
    • IGameStateCalculator<S, PS, A> gameSpecificStateCalculator
    • A dummyGameSpecificAction
  • Create a class/classes implementing each interface in above three points. While implementing these interfaces, create different classes implementing IGameState, IGameAction, IGSPlayerState.
  • IGameEventListener<S, PS, A> where A:IGameAction where S:IGameState where PS: IGSPlayerState interface needs to be implemented so as to update the game/player’s UI depending on remote event/state. Description of non-trivial Api of IGameEventListener are:

    • void onReceivedGameState(GenericGameState<S, PS> gameState); //update the game UI with this state and enable/disable interaction if required. It is called to synchronize the state.

    • void onSynchronizingGameState(); //disable the UI/interaction and show appropriate messages. It is called after reconnection to the game server.

    • void onDisconnected(); //disable the UI/interaction and show appropriate messages. It is called when I get disconnected from the game server.

    • void onConnected(); //enable the UI/interaction and show/remove messages. It is called when I get re-connected to the game server.

    • void onPlayerLeft(string userId);//It is called when a player (including local player) is marked left. A player is marked left when disconnectionTimeout(30 seconds) happens after disconnection.

    • void onAppPauseState(bool paused, double pauseSec = 0); //It is called when app pauses/resumes. When the app resumes, pauseSec represents pauseDuration.

    • void onGameAction(GenericGameAction<A> action); //It is called when a remote player/game’ action comes to the client. Process the action appropriately to update the UI.

    • While processing the action, updateGameStateFromLocalGame, updatePlayerStateFromLocalGame, updateCurrentUserIdFromLocalGame (Section 4.2.d.iii,Section 4.2.d.iv,Section 4.2.d.v respectively) of IGenericGameManager should be called.

    • After the action is processed successfully, do call onRemoteActionProcessed(Section 4.2.d.ii) of IGenericGameManager, so that the framework can notify the next remote action via onGameAction(GenericGameAction<A> action).

  • IGameStateCalculator<S, PS, A> where S:IGameState where A: IGameAction where PS: IGSPlayerState interface needs to be implemented so that the framework can fire/notify appropriate events/actions for all players at desired time. Description of non-trivial Api of IGameStateCalculator are:
* `S getInitialGameState(JoyrideGame.JRGameInfo JRGameInfo, MultiPlayer.IRemotePlayer[] remotePlayers);` //should return game specific state data which will be same for all players. It is used to create an initial instance of `GenericGameState<S, PS> where S: IGameState where PS:IGSPlayerState` which is used by the framework to store the game’s state.


* `GenericGameAction<A> getActionToStartGameWithStartingState(GenericGameState<S, PS> gameState, string firstUserId, int serverTime);` //It is called by master client only to send start event to each player including itself, so that game can start with same state/action at almost same time in each client. Eg- For a chain-reaction game, it should return action corresponding to the start turn for the first player. For a Uno game, it should return action corresponding to the start turn and initially dealt random cards. For chain-reaction game, its implementation is:

* `GenericGameAction<BaseCRActionData> action = new` `GenericGameAction<BaseCRActionData>("", serverTime, false);` `action.gameSpecificActionData = new ChainRNextTurnData(firstUserId);`

return action;

    * `GenericGameAction<A> getNextActionIfTimeout(GenericGameState<S,PS> gameState, int serverTime);` //called by master client only to fire next action on behalf of a GameAI player and a player who got disconnected.

* `bool isValidActionToUpdateState(GenericGameAction<A> action, GenericGameState<S,PS> gameState);` //return as if remote action is valid or not for current game specific state of the game.

* `PS getInitialPlayerState(string userId, int serverTime, int playersCount);` //it should return the game specific player state for the requested userId. It is used to create an initial instance of `GenericGameState<S, PS> where S: IGameState where PS:IGSPlayerState` which is used by the framework to store the game’s state. For chain-reaction game, its implementation is:

return new CRInGamePlayerState(serverTime, playersCount, false, false);

  • API present in IGenericGameManager<S,PS,A> where A:IGameAction where S:IGameState where PS:IGSPlayerState should be called from appropriate places in the game-core codebase. Its non-trivial API are:

    • GenericGameAction<A> onLocalUserAction(A gameSpecificActionData, bool isTimeout);//call this method to notify action performed by the local user to remote players.

    • void onRemoteActionProcessed();//This method must be called after a remote action/event received in Section 4.2.b.vii is processed completely by the local client.

    • void updateGameStateFromLocalGame(S gameSpecificStateFromLocalGame);//This method may be called during processing of remote action/event received in Section 4.2.b.vii

    • void updatePlayerStateFromLocalGame(Dictionary<string,PS> gameSpecificPlayerState);//This method may be called during processing of remote action/event received in Section 4.2.b.vii

    • void updateCurrentUserIdFromLocalGame(string userId);//This method may be called during processing of remote action/event received in Section 4.2.b.vii

    • bool isGameStarted();

    • int currentServerTime();//It returns current server time which can even be negative. Use it to get time difference or populate playerAction/game’s serverTime only.

    • bool isPlayerLeft(string userId);//It returns true if player is marked Left(player is marked Left when disconnectionTimeout(30 seconds) happens after a player gets disconnected)

    • bool isConnectedToServerWithProperState();//returns true if game is started, local client is connected with recent game state.

    • PS getGameSpecificPlayerState(string userId);

  • Don’t modify the property of GenericGameState and GenericGameAction from anywhere.

  • IGameAction interface is meant to represent any action in the game, which generally corresponds to a player. Description of non-trivial Api of IGameAction are:

    • Byte getUniqueCode(); //return unique value from 2 to 199 for each action.
    • bool shouldSendStateAlso();//return true for one or more IGameAction. If true, we append local gameState to the action data while sending over the server. Before processing this action on a remote client, we first update the gameState so that all clients are in sync
  • IGSPlayerState interface is meant to represent the game specific state of a player. Description of non-trivial Api of IGSPlayerState are:

    • int startTime();should return the start time of the player’s current state. It generally is the timeStamp of the last action taken by the player
  • IGameState interface is meant to represent the game state like the state of board, which is not part of IGSPlayerState


Class

JRGameInfo

ClassData Type
myInfoJRUserInfo
opponentArrayJRUserInfo
userCountryCodestring
serverRegionstring
roomNamestring
roundCountint
durationint
difficultyLevelint
isFuebool
gameVariantIdstring
igameSpecificConfigJsonstring
musicVolumefloat
activeMultiplierstring
sfxVolumefloat
multiplierRewardDurationint
boosterTypestring
forcedGameAIRequiredbool

JRUserInfo

ClassData Type
userIdstring
userNamestring
profileUrlstring
positionint
activeMultiplierstring
gameUserIdstring
gameLevelint
boosterTypestring
abandonGameLevelint