Using the Scalable Server ========================= Scalable Server documentation ----------------------------- Please refer to the documentation available at http://ssdoc.asmodee.net/ Dependencies ------------ Protobuf C# """"""""""" Google provides Protobuf via GitHub: https://github.com/protocolbuffers/protobuf. We developed SDK 5.0 using Protobuf v3.6.1: * Go to https://github.com/protocolbuffers/protobuf/releases/tag/v3.6.1 * Download *protobuf-csharp-3.6.1.zip* * Extract sub directory *csharp/src/Google.Protobuf* and import it in your Unity project Protobuf Unity Compiler """"""""""""""""""""""" Protobuf lets you describe the format of your data in *.proto* files, then you generate the corresponding C# classes by calling *protoc*. ProtobufUnityCompiler available at https://github.com/5argon/protobuf-unity/blob/master/Editor/ProtobufUnityCompiler.cs eases this process by automatically calling *protoc* every time you modify a *.proto* file. Setup ----- NetworkParameters """"""""""""""""" Verify your connection parameters in `Network Parameters `_ under *Scalable Server* section. CoreApplication """"""""""""""" In your `CoreApplication `_ component, you should set the *Use Scalable Server* parameter to *true*. Usage ----- ScalableServerManager """"""""""""""""""""" ``ScalableServerManager`` is the entry point for all the services linked with the Scalable Server. You can access it through the ``CoreApplication`` : .. code:: csharp var scalableServerManager = CoreApplication.Instance.ScalableServerManager; The ``ScalableServerManager`` contains a ``ServerConnection`` wich is the low level API to send and receive requests. It also provides *workflows* following the `Scalable Server Documentation `_. CommunityHub """""""""""" A simple way to connect to the Scalable Server is to `require an Online Status `_ with: * ``InternetConnectionWithScalableServerConnectionOptionalStatus`` * ``InternetConnectionWithScalableServerConnectionRequiredStatus`` will prompt the SSO ServerConnection """""""""""""""" ``ServerConnection`` is the base layer where everything happen. Your main entry point is ``Connect`` allowing you to establish a connection with the Scalable Server and triggering the authentication of the player. When leaving an online section of the game, you may want to ``Disconnect`` or ``DisconnectAfterDelay``. When connected to the Scalable Server, the client sends and receives requests. The complete `Request Reference `_ is available as C# helper methods (C -> S) and events (S -> C). .. code:: csharp public enum ConnectionState { FailedToConnect, NotConnected, Connecting, Connected } public ConnectionState ConnectionState; public void Connect(); public void Reconnect(); public void Disconnect(bool disconnectedOnNetworkLoss = false); public void DisconnectAfterDelay(); public void PreventFromDelayedDisconnection(); // Server -> Client public event Action GameStatusReportRequestReceived; ... ... // Client -> Server public long SendWhatsNewPussycatRequest(long? gameId = null, bool summaryDataOnly = false) {...} ... ... Authentication Workflow """"""""""""""""""""""" Reference: `Scalable Server Documentation `_. Authenticating the user is done automatically when a connection is established. You can subscribe to ``AuthenticationState`` modifications, and access data about the ``AuthenticatedPlayer``. .. code:: csharp public enum AuthenticationState { NotAuthenticated, Authenticating, Authenticated } public AuthenticationState AuthenticationState; public event Action Authenticated; public event Action DeAuthenticated; public event Action FailedToAuthenticate; public Player AuthenticatedPlayer { get; private set; } Chat Workflow """"""""""""" References: `in lobby chat workflow documentation `_, `in game chat workflow documentation `_ The API is simple, but we also provide a `Chat Community Hub Content `_ for a simpler integration. .. code:: csharp public event Action ChatReceived; public event Action> ChatHistoryReceived; public void RefreshHistory(long? gameID = null) {...} public void SendMessage(string chat, long? gameId, List recipientIds = null, int code = 0) {...} Game Workflow """"""""""""" Reference: `Scalable Server Documentation `_. Receiving game events: .. code:: csharp public delegate void GameCreatedListener(int invitedBy, int localPlayerId, GameDetails onComplete); public event GameCreatedListener gameCreatedListener; // Standard Game Turn public delegate void ActionRequiredListener(long gameId, byte[] state, List players, List nextPlayerIds, int turnIndex, int invitedBy, GameClock playerClock, List userData); public event ActionRequiredListener actionRequiredListener; // - Broadcast public delegate void GameStateUpdatedListener(long gameId, int playerId, int turnIndex, byte[] state, List clock); public event GameStateUpdatedListener gameStateUpdatedListener; // Robots public delegate void PlayerTimeoutListener(long gameId, byte[] state, int offenderId, List players, List nextPlayerIds, PlayerTimeoutRequest.Types.PlayerStatus status, int turnIndex, GameStatus gameStatus, List userData); public event PlayerTimeoutListener playerTimeoutListener; public delegate void PlayerReplacedListener(long gameId, int playerId, bool becomeRobot, PlayerTimeoutRequest.Types.PlayerStatus status); public event PlayerReplacedListener playerReplacedListener; // Simultaneous Game Turn public delegate void UserDataUpdateRequiredListener(long gameId, int turnIndex, List participant, byte[] state); public event UserDataUpdateRequiredListener userDataUpdateRequiredListener; // - Broadcast public delegate void GameUserDataUpdatedListener(long gameId, int playerId, int turnIndex, List userData); public event GameUserDataUpdatedListener gameUserDataUpdatedListener; // Multicast public delegate void ClientDataListener(long gameId, byte[] data, Player sender); public event ClientDataListener clientDataListener; // Abort public delegate void GameAbortedListener(long gameId, List abortingPlayerIds, List rankingUpdate, List karmaUpdate, byte[] data); public event GameAbortedListener gameAbortedListener; public void PlayerClocksStatus(Action> completion, long gameId) { ... } Sending game data: .. code:: csharp public void CommitAction(Action completion, long gameId, int localPlayerId, byte[] nextState, List nextPlayerIds, byte[] nextSummaryData = null, int turnIndex = 0, bool nextSimultaneous = false, List simultaneousPlayers = null, bool broadcast = false) { ... } public void UpdateUserData(Action completion, long gameId, byte[] userData, int localPlayerId, bool expectMoreData, bool broadcast) { ... } public void MulticastData(Action completion, long gameId, byte[] data, List recipientIds, bool presentPlayersOnly) { ... } public void GameForfeit(Action completion, long gameId) { ... } public void GameOver(Action, List> completion, long gameId, List finalScore, List achievementsAwarded = null, byte[] data = null) { ... } public void GameOutcomeConfirmation(long gameId, int localPlayerId) { ... } public void GameAbortedConfirmation(long gameId, int localPlayerId) { ... } In Game Presence: .. code:: csharp public enum Presence { unknown, absent, present, inGame } public event Action> PresenceDidChange; public Presence PlayerInGamePresence(int playerId) { ... } public void SwitchToGame(long gameId) { ... } public void LeaveGame(long gameId) { ... } General Workflow """""""""""""""" Reference: `Scalable Server Documentation `_. All non-specific services are provided through the General Workflow. .. code:: csharp public void GetGameInformation(Action completion, long gameId, bool summaryDataOnly = false) { ... } public void GetCurrentGamesInformation(Action> completion, bool summaryDataOnly = false) { ... } // Buddy List / Ignore List public void GetBuddyList(Action> completion, List webCardFilter = null) { ... } public void AddBuddy(Action completion, int playerId) { ... } public void RemoveBuddy(Action completion, int playerId) { ... } public void GetIgnoreList(Action> completion, List webCardFiter = null) { ... } public void AddIgnore(Action completion, int playerId) { ... } public void RemoveIgnore(Action completion, int playerId) { ... } // Server Statistics public ServerStatistics? LatestServerStatistics; public event Action ServerStatisticsDidChange; public void AskServerStatistics(bool subscribe) { ... } // Presence public enum Presence { unknown, absent, present } public event Action> PresenceDidChange; public Presence PlayerPresence(int playerId) { ... } public void RegisterPresence(List playerIds) { ... } public void UnregisterPresence(List playerIds) { ... } public void SubscribePresenceService() { ... } public void UnsubscribePresenceService() { ... } // Invitation public delegate void InvitationAnsweredListener(long gameId, Player invitee, bool accept); public event InvitationAnsweredListener invitationAnsweredListener; public void EngageGameWithFriends(Action completion, GameConfiguration gameConfiguration, List friends, byte[] userData = null, byte[] initialState = null, byte[] initialStateSummary = null) { ... } public void AnswerInvitation(Action completion, long gameId, bool accept, byte[] userData = null) { ... } Heartbeat Workflow """""""""""""""""" Reference: `Scalable Server Documentation `_. In order to keep a connection live, the Scalable Server requires to send requests regularly. The role of ``HeartbeatWorkflow`` is to send the *Ping* request and to alert if it takes too long to receive a response. .. code:: csharp public enum HeartbeatState { Failure, Disabled, Unstable, Stable } public long RequestRoundTripTime { get; private set; } public event Action HeartbeatStateDidChange; public HeartbeatState HeartbeatState; Lobby Workflow """""""""""""" Lobby State: .. code:: csharp public enum LobbyState { NotInLobby, EnteringLobby, ExitingLobby, InLobby } public LobbyState LobbyState; public event Action EnteredLobby; public event Action ExitedLobby; public bool AutomaticallyEnterLobbyAgainAfterReAuthentication { get; set; } = true; public void EnterLobby(Action completion = null, bool forceSendingRequest = false) { ... } public void ExitLobby(Action completion = null, bool forceSendingRequest = false) { ... } Lobby Players: .. code:: csharp public event Action> LobbyPlayersUpdated; public IList LobbyPlayers; public void GetPlayerInformation(Action completion, int playerId) { ... } Open Games: .. code:: csharp public event Action> LobbyGamesUpdated; public event Action> LobbyJoinedGameIdsUpdated; public event Action NewPlayer; public event Action PlayerLeftGame; public IList LobbyGames; public IList LobbyJoinedGameIds; public void CreateGame(Action completion, GameConfiguration gameConfiguration, string password = "", byte[] userData = null, byte[] initialState = null, byte[] initialStateSummary = null) { ... } public void JoinGame(Action completion, long gameId, string password = "", byte[] userData = null) { ... } public void LeaveGame(Action completion, long gameId) { ... } public void StartGame(Action completion, long gameId, bool fillWithRobots = false, byte[] data = null) { ... } Observable Games: .. code:: csharp public event Action> ObservableGamesUpdated; public IList ObservableGames; public void SubscribeToObservableGames(Action completion, bool subscribe = true) { ... } public void UnSubscribeToObservableGames(Action completion) { ... } public void StartObserveGame(Action completion, long gameId, bool observe = true) { ... } public void StopObserveGame(Action completion, long gameId) { ... } LobbyPresenceManager """""""""""""""""""" After having entered the Lobby, we can monitor the presence of specific players. This is useful to present the availability of the player's buddies. .. code:: csharp public interface IPresenceSubscriber { void UpdatePresence(int playerWWWId, PresenceStatus newStatus); } public void Subscribe(int playerWWWId, IPresenceSubscriber subscriber) { ... } public void Subscribe(List playerWWWIds, IPresenceSubscriber subscriber) { ... } public void Unsubscribe(int playerWWWId, IPresenceSubscriber subscriber) { ... } public void Unsubscribe(List playerWWWIds, IPresenceSubscriber subscriber) { ... } public enum PresenceStatus { Unknown, Absent, Away, Present } public PresenceStatus GetPresenceStatus(int playerWWWId) { ... }