/// <summary> /// Uses the OperationResponses provided by the server to advance the internal state and call ops as needed. /// </summary> /// <remarks> /// When this method finishes, it will call your OnOpResponseAction (if any). This way, you can get any /// operation response without overriding this class. /// /// To implement a more complex game/app logic, you should implement your own class that inherits the /// LoadBalancingClient. Override this method to use your own operation-responses easily. /// /// This method is essential to update the internal state of a LoadBalancingClient, so overriding methods /// must call base.OnOperationResponse(). /// </remarks> /// <param name="operationResponse">Contains the server's response for an operation called by this peer.</param> public virtual void OnOperationResponse(OperationResponse operationResponse) { // if (operationResponse.ReturnCode != 0) this.DebugReturn(DebugLevel.ERROR, operationResponse.ToStringFull()); // use the "secret" or "token" whenever we get it. doesn't really matter if it's in AuthResponse. if (operationResponse.Parameters.ContainsKey(ParameterCode.Secret)) { if (this.AuthValues == null) { this.AuthValues = new AuthenticationValues(); //this.DebugReturn(DebugLevel.ERROR, "Server returned secret. Created AuthValues."); } this.AuthValues.Token = operationResponse[ParameterCode.Secret] as string; } switch (operationResponse.OperationCode) { case OperationCode.FindFriends: if (operationResponse.ReturnCode != 0) { this.DebugReturn(DebugLevel.ERROR, "OpFindFriends failed: " + operationResponse.ToStringFull()); this.isFetchingFriendList = false; break; } bool[] onlineList = operationResponse[ParameterCode.FindFriendsResponseOnlineList] as bool[]; string[] roomList = operationResponse[ParameterCode.FindFriendsResponseRoomIdList] as string[]; this.FriendList = new List<FriendInfo>(this.friendListRequested.Length); for (int index = 0; index < this.friendListRequested.Length; index++) { FriendInfo friend = new FriendInfo(); friend.Name = this.friendListRequested[index]; friend.Room = roomList[index]; friend.IsOnline = onlineList[index]; this.FriendList.Insert(index, friend); } this.friendListRequested = null; this.isFetchingFriendList = false; this.friendListTimestamp = Environment.TickCount; if (this.friendListTimestamp == 0) { this.friendListTimestamp = 1; // makes sure the timestamp is not accidentally 0 } break; case OperationCode.Authenticate: { if (operationResponse.ReturnCode != 0) { this.DebugReturn(DebugLevel.ERROR, operationResponse.ToStringFull() + " Server: " + this.Server + " Address: " + this.loadBalancingPeer.ServerAddress); switch (operationResponse.ReturnCode) { case ErrorCode.InvalidAuthentication: this.DisconnectedCause = DisconnectCause.InvalidAuthentication; break; case ErrorCode.CustomAuthenticationFailed: this.DisconnectedCause = DisconnectCause.CustomAuthenticationFailed; break; case ErrorCode.InvalidRegion: this.DisconnectedCause = DisconnectCause.InvalidRegion; break; case ErrorCode.MaxCcuReached: this.DisconnectedCause = DisconnectCause.MaxCcuReached; break; case ErrorCode.OperationNotAllowedInCurrentState: this.DisconnectedCause = DisconnectCause.OperationNotAllowedInCurrentState; break; } this.State = ClientState.Disconnecting; this.Disconnect(); break; // if auth didn't succeed, we disconnect (above) and exit this operation's handling } if (this.Server == ServerConnection.NameServer || this.Server == ServerConnection.MasterServer) { if (operationResponse.Parameters.ContainsKey(ParameterCode.UserId)) { this.UserId = (string)operationResponse.Parameters[ParameterCode.UserId]; this.DebugReturn(DebugLevel.INFO, string.Format("Setting UserId sent by Server:{0}", this.UserId)); } if (operationResponse.Parameters.ContainsKey((byte)233)) { this.NickName = (string)operationResponse.Parameters[(byte)233]; this.DebugReturn(DebugLevel.INFO, string.Format("Setting Nickname sent by Server:{0}", this.NickName)); } } if (this.Server == ServerConnection.NameServer) { // on the NameServer, authenticate returns the MasterServer address for a region and we hop off to there this.MasterServerAddress = operationResponse[ParameterCode.Address] as string; this.DisconnectToReconnect(); } else if (this.Server == ServerConnection.MasterServer) { this.State = ClientState.ConnectedToMaster; if (this.AutoJoinLobby) { this.loadBalancingPeer.OpJoinLobby(this.CurrentLobby); } } else if (this.Server == ServerConnection.GameServer) { this.State = ClientState.ConnectingToGameserver; if (this.lastJoinType == JoinType.JoinRoom || this.lastJoinType == JoinType.JoinRandomRoom) { // if we just "join" the game, do so. if we "join-or-create", we have to set the createIfNotExists parameter to true this.State = ClientState.Joining; this.OpJoinRoom(this.CurrentRoom.Name, this.lastJoinActorNumber); } else if(this.lastJoinType == JoinType.JoinOrCreateRoom) { this.State = ClientState.Joining; this.OpJoinOrCreateRoom(this.CurrentRoom.Name, this.lastJoinActorNumber, this.createRoomOptions, this.CurrentLobby); } else if (this.lastJoinType == JoinType.CreateRoom) { this.State = ClientState.Joining; // yes, "joining" even though we technically create the game now on the game server this.OpCreateRoom(this.CurrentRoom.Name, this.createRoomOptions, this.CurrentLobby); } break; } break; } case OperationCode.GetRegions: this.AvailableRegions = operationResponse[ParameterCode.Region] as string[]; this.AvailableRegionsServers = operationResponse[ParameterCode.Address] as string[]; break; case OperationCode.Leave: //this.CleanCachedValues(); // this is done in status change on "disconnect" this.State = ClientState.DisconnectingFromGameserver; this.loadBalancingPeer.Disconnect(); break; case OperationCode.JoinLobby: this.State = ClientState.JoinedLobby; break; case OperationCode.JoinRandomGame: // this happens only on the master server. on gameserver this is a "regular" join case OperationCode.CreateGame: case OperationCode.JoinGame: { if (this.Server == ServerConnection.GameServer) { this.GameEnteredOnGameServer(operationResponse); } else { if (operationResponse.ReturnCode == ErrorCode.NoRandomMatchFound) { // this happens only for JoinRandomRoom // TODO: implement callback/reaction when no random game could be found (this is no bug and can simply happen if no games are open) this.State = ClientState.JoinedLobby; // TODO: maybe we have to return to another state here (if we didn't join a lobby) break; } // TODO: handle more error cases if (operationResponse.ReturnCode != 0) { if (this.loadBalancingPeer.DebugOut >= DebugLevel.ERROR) { this.DebugReturn(DebugLevel.ERROR, string.Format("Getting into game failed, client stays on masterserver: {0}.", operationResponse.ToStringFull())); } this.State = ClientState.JoinedLobby; // TODO: maybe we have to return to another state here (if we didn't join a lobby) break; } this.GameServerAddress = (string)operationResponse[ParameterCode.Address]; string gameId = operationResponse[ParameterCode.RoomName] as string; if (!string.IsNullOrEmpty(gameId)) { // is only sent by the server's response, if it has not been sent with the client's request before! this.CurrentRoom.Name = gameId; } this.DisconnectToReconnect(); } break; } } if (this.OnOpResponseAction != null) this.OnOpResponseAction(operationResponse); }
/// <summary> /// Uses the OperationResponses provided by the server to advance the internal state and call ops as needed. /// </summary> /// <remarks> /// When this method finishes, it will call your OnOpResponseAction (if any). This way, you can get any /// operation response without overriding this class. /// /// To implement a more complex game/app logic, you should implement your own class that inherits the /// LoadBalancingClient. Override this method to use your own operation-responses easily. /// /// This method is essential to update the internal state of a LoadBalancingClient, so overriding methods /// must call base.OnOperationResponse(). /// </remarks> /// <param name="operationResponse">Contains the server's response for an operation called by this peer.</param> public virtual void OnOperationResponse(OperationResponse operationResponse) { // if (operationResponse.ReturnCode != 0) this.DebugReturn(DebugLevel.ERROR, operationResponse.ToStringFull()); // use the "secret" or "token" whenever we get it. doesn't really matter if it's in AuthResponse. if (operationResponse.Parameters.ContainsKey(ParameterCode.Secret)) { if (this.AuthValues == null) { this.AuthValues = new AuthenticationValues(); //this.DebugReturn(DebugLevel.ERROR, "Server returned secret. Created AuthValues."); } this.AuthValues.Token = operationResponse[ParameterCode.Secret] as string; } switch (operationResponse.OperationCode) { case OperationCode.FindFriends: if (operationResponse.ReturnCode != 0) { this.DebugReturn(DebugLevel.ERROR, "OpFindFriends failed: " + operationResponse.ToStringFull()); this.isFetchingFriendList = false; break; } bool[] onlineList = operationResponse[ParameterCode.FindFriendsResponseOnlineList] as bool[]; string[] roomList = operationResponse[ParameterCode.FindFriendsResponseRoomIdList] as string[]; this.FriendList = new List<FriendInfo>(this.friendListRequested.Length); for (int index = 0; index < this.friendListRequested.Length; index++) { FriendInfo friend = new FriendInfo(); friend.Name = this.friendListRequested[index]; friend.Room = roomList[index]; friend.IsOnline = onlineList[index]; this.FriendList.Insert(index, friend); } this.friendListRequested = null; this.isFetchingFriendList = false; this.friendListTimestamp = Environment.TickCount; if (this.friendListTimestamp == 0) { this.friendListTimestamp = 1; // makes sure the timestamp is not accidentally 0 } break; case OperationCode.Authenticate: { if (operationResponse.ReturnCode != 0) { this.DebugReturn(DebugLevel.ERROR, operationResponse.ToStringFull() + " Server: " + this.Server + " Address: " + this.loadBalancingPeer.ServerAddress); switch (operationResponse.ReturnCode) { case ErrorCode.InvalidAuthentication: this.DisconnectedCause = DisconnectCause.InvalidAuthentication; break; case ErrorCode.CustomAuthenticationFailed: this.DisconnectedCause = DisconnectCause.CustomAuthenticationFailed; break; case ErrorCode.InvalidRegion: this.DisconnectedCause = DisconnectCause.InvalidRegion; break; case ErrorCode.MaxCcuReached: this.DisconnectedCause = DisconnectCause.MaxCcuReached; break; case ErrorCode.OperationNotAllowedInCurrentState: this.DisconnectedCause = DisconnectCause.OperationNotAllowedInCurrentState; break; } this.State = ClientState.Disconnecting; this.Disconnect(); break; // if auth didn't succeed, we disconnect (above) and exit this operation's handling } if (this.Server == ServerConnection.NameServer || this.Server == ServerConnection.MasterServer) { if (operationResponse.Parameters.ContainsKey(ParameterCode.UserId)) { string incomingId = (string)operationResponse.Parameters[ParameterCode.UserId]; if (!string.IsNullOrEmpty(incomingId)) { this.UserId = incomingId; this.DebugReturn(DebugLevel.INFO, string.Format("Setting UserId sent by Server:{0}", this.UserId)); } } if (operationResponse.Parameters.ContainsKey((byte)233)) { this.NickName = (string)operationResponse.Parameters[(byte)233]; this.DebugReturn(DebugLevel.INFO, string.Format("Setting Nickname sent by Server:{0}", this.NickName)); } } if (this.Server == ServerConnection.NameServer) { // on the NameServer, authenticate returns the MasterServer address for a region and we hop off to there this.MasterServerAddress = operationResponse[ParameterCode.Address] as string; this.DisconnectToReconnect(); } else if (this.Server == ServerConnection.MasterServer) { this.State = ClientState.ConnectedToMaster; if (this.AutoJoinLobby) { this.loadBalancingPeer.OpJoinLobby(this.CurrentLobby); } } else if (this.Server == ServerConnection.GameServer) { this.State = ClientState.Joining; this.enterRoomParamsCache.PlayerProperties = this.LocalPlayer.CustomProperties; this.enterRoomParamsCache.OnGameServer = true; if (this.lastJoinType == JoinType.JoinRoom || this.lastJoinType == JoinType.JoinRandomRoom || this.lastJoinType == JoinType.JoinOrCreateRoom) { this.loadBalancingPeer.OpJoinRoom(this.enterRoomParamsCache); } else if (this.lastJoinType == JoinType.CreateRoom) { this.loadBalancingPeer.OpCreateRoom(this.enterRoomParamsCache); } break; } break; } case OperationCode.GetRegions: this.AvailableRegions = operationResponse[ParameterCode.Region] as string[]; this.AvailableRegionsServers = operationResponse[ParameterCode.Address] as string[]; break; case OperationCode.Leave: //this.CleanCachedValues(); // this is done in status change on "disconnect" this.State = ClientState.DisconnectingFromGameserver; this.loadBalancingPeer.Disconnect(); break; case OperationCode.JoinLobby: this.State = ClientState.JoinedLobby; break; case OperationCode.LeaveLobby: this.State = ClientState.ConnectedToMaster; break; case OperationCode.JoinRandomGame: // this happens only on the master server. on gameserver this is a "regular" join case OperationCode.CreateGame: case OperationCode.JoinGame: { if (this.Server == ServerConnection.GameServer) { this.GameEnteredOnGameServer(operationResponse); } else { // TODO: handle more error cases if (operationResponse.ReturnCode != 0) { // TODO: error callbacks?! at the moment, developers have to implement this.OnOpResponseAction to get error cases of this. //if (operationResponse.ReturnCode == ErrorCode.NoRandomMatchFound) //{ // // this happens only for JoinRandomRoom // break; //} if (this.loadBalancingPeer.DebugOut >= DebugLevel.ERROR) { this.DebugReturn(DebugLevel.ERROR, string.Format("Getting into game failed, client stays on masterserver: {0}.", operationResponse.ToStringFull())); } break; } this.GameServerAddress = (string)operationResponse[ParameterCode.Address]; string roomName = operationResponse[ParameterCode.RoomName] as string; if (!string.IsNullOrEmpty(roomName)) { this.enterRoomParamsCache.RoomName = roomName; } this.DisconnectToReconnect(); } break; } } if (this.OnOpResponseAction != null) this.OnOpResponseAction(operationResponse); }