/// <summary> /// Call this function when an error occurs during the simulation stage. /// </summary> /// <param name="reason">The reason of the error.</param> /// <param name="customData">Custom data about the error.</param> public void SimulationStageError(string reason, byte[] customData) { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } /// If we are currently finishing the simulation --> do nothing if (this.hostLeft) { return; } /// Stop the alarm clocks StopCommitTimeoutClock(); StopSimulationFrameClock(); UnregisterAllCommitMonitors(); /// Send the DSS_SIM_ERROR message to the others RCPackage errPackage = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_SIM_ERROR); errPackage.WriteString(0, reason); errPackage.WriteByteArray(1, customData); this.root.LobbyIface.SendPackage(errPackage); /// Quit from the DSS immediately this.root.SimulatorIface.SimulationError(reason, customData); this.root.EventQueue.ExitEventLoop(); }
/// <summary> /// Sends reports about the given line states to all connected clients and to the listener of this server. /// </summary> private void SendLineStateReports(LobbyLineState[] lineStates) { for (int i = 0; i < this.connections.Length; i++) { if (this.connections[i].ConnectionState == LobbyConnectionState.Connected) { RCPackage lineStateReport = RCPackage.CreateNetworkControlPackage(Network.FORMAT_LOBBY_LINE_STATE_REPORT); lineStateReport.WriteShort(0, (short)(i + 1)); /// The ID of the client that receives the report. byte[] lineStatesBytes = new byte[lineStates.Length]; for (int j = 0; j < lineStatesBytes.Length; j++) { lineStatesBytes[j] = (byte)lineStates[j]; } lineStateReport.WriteByteArray(1, lineStatesBytes); /// The list of the line states on this server. /// Send the package if (!this.connections[i].SendPackage(lineStateReport)) { /// In case of error, shutdown the connection and notify the other clients again. this.connections[i].Shutdown(); this.connections[i].LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); } } } this.listener.LineStateReport(0, lineStates); }
/// <summary> /// Creates the package that contains the description of the isometric tiles of the given map. /// </summary> /// <param name="map">Reference to the map.</param> /// <returns>The data package that contains the description of the isometric tiles of the given map.</returns> private RCPackage CreateIsoTileListPackage(IMapAccess map) { RCPackage isotileList = RCPackage.CreateCustomDataPackage(MapFileFormat.ISOTILE_LIST); /// Create the terrain type index table. List <string> terrainTypeList = new List <string>(); Dictionary <ITerrainType, int> terrainTypeIndexTable = new Dictionary <ITerrainType, int>(); int terrainTypeIndex = 0; foreach (ITerrainType terrainType in map.Tileset.TerrainTypes) { terrainTypeList.Add(terrainType.Name); terrainTypeIndexTable.Add(terrainType, terrainTypeIndex); terrainTypeIndex++; } isotileList.WriteStringArray(0, terrainTypeList.ToArray()); /// Create the packages of the isometric tiles. RCSet <IIsoTile> processedIsoTiles = new RCSet <IIsoTile>(); List <RCPackage> isotilePackages = new List <RCPackage>(); int isotileInfoLength = 0; for (int row = 0; row < map.Size.Y; row++) { for (int column = 0; column < map.Size.X; column++) { IIsoTile currIsoTile = map.GetQuadTile(new RCIntVector(column, row)).PrimaryIsoTile; if (!processedIsoTiles.Contains(currIsoTile)) { RCPackage isotilePackage = RCPackage.CreateCustomDataPackage(MapFileFormat.ISOTILE); isotilePackage.WriteShort(0, (short)column); isotilePackage.WriteShort(1, (short)row); isotilePackage.WriteByte(2, (byte)terrainTypeIndexTable[currIsoTile.Type.TerrainA]); isotilePackage.WriteByte(3, currIsoTile.Type.TerrainB != null ? (byte)terrainTypeIndexTable[currIsoTile.Type.TerrainB] : (byte)0); isotilePackage.WriteByte(4, (byte)currIsoTile.Type.Combination); isotilePackage.WriteByte(5, (byte)currIsoTile.VariantIdx); isotilePackages.Add(isotilePackage); processedIsoTiles.Add(currIsoTile); isotileInfoLength += isotilePackage.PackageLength; } } } /// Write the isometric tile packages into the final package byte[] isotileInfoBytes = new byte[isotileInfoLength]; int offset = 0; foreach (RCPackage isotilePackage in isotilePackages) { offset += isotilePackage.WritePackageToBuffer(isotileInfoBytes, offset); } isotileList.WriteByteArray(1, isotileInfoBytes); return(isotileList); }
/// <summary> /// Sends a DSS_LEAVE message to the other side of this session. /// </summary> /// <param name="reason">The reason of leaving the DSS.</param> /// <param name="customData">Custom data about leaving the DSS.</param> public void SendLeaveMsg(string reason, byte[] customData) { if (this.sm.CurrentState == this.SendingSetupStepRQ) { RCPackage leaveMsg = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_LEAVE); leaveMsg.WriteString(0, reason); leaveMsg.WriteByteArray(1, customData); this.manager.HostRoot.Lobby.SendControlPackage(leaveMsg, this.channel.Index + 1); } }
/// <summary> /// Call this function to initialize the UI for the setup stage and send reset messages to the connected guests. /// </summary> private void FirstSetupStep(IDssGuestChannel[] channelsToGuests) { /// Reset the UI this.simulator.GetPlayer(0).Reset(); this.simulator.GetPlayer(0).Activate(); this.uiCallMarshal.SetMainControlStatus(FormStatus.HostSide); this.uiCallMarshal.SelectNewHostColor(this.simulator.GetPlayer(0).Color); this.uiCallMarshal.SetHostControlStatus(HostControlStatus.AccessGranted); this.previousChannelStates = new DssChannelState[channelsToGuests.Length]; for (int i = 0; i < this.previousChannelStates.Length; i++) { this.simulator.GetPlayer(i + 1).Reset(); this.previousChannelStates[i] = channelsToGuests[i].ChannelState; if (channelsToGuests[i].ChannelState == DssChannelState.CHANNEL_OPENED) { this.uiCallMarshal.SetGuestControlStatus(i, GuestControlStatus.HostSideOpened); } else if (channelsToGuests[i].ChannelState == DssChannelState.CHANNEL_CLOSED) { this.uiCallMarshal.SetGuestControlStatus(i, GuestControlStatus.HostSideClosed); } else if (channelsToGuests[i].ChannelState == DssChannelState.GUEST_CONNECTED) { this.simulator.GetPlayer(i + 1).Activate(); this.uiCallMarshal.SelectNewGuestColor(i, this.simulator.GetPlayer(i + 1).Color); this.uiCallMarshal.SetGuestControlStatus(i, GuestControlStatus.HostSideEngaged); } } /// Send reset messages for (int i = 0; i < channelsToGuests.Length; i++) { if (channelsToGuests[i].ChannelState == DssChannelState.GUEST_CONNECTED) { byte[] colors = new byte[this.simulator.MaxNumOfPlayers]; int[] xCoords = new int[this.simulator.MaxNumOfPlayers]; int[] yCoords = new int[this.simulator.MaxNumOfPlayers]; for (int j = 0; j < colors.Length; j++) { colors[j] = (byte)this.simulator.GetPlayer(j).Color; xCoords[j] = this.simulator.GetPlayer(j).Position.X; yCoords[j] = this.simulator.GetPlayer(j).Position.Y; } RCPackage reset = RCPackage.CreateNetworkControlPackage(TestClientMessages.RESET); reset.WriteByteArray(0, colors); reset.WriteIntArray(1, xCoords); reset.WriteIntArray(2, yCoords); this.rqs[i].Add(reset); } } }
/// <summary> /// Leaves the DSS. /// </summary> private void LeaveDss() { /// Send the DSS_LEAVE message to the host. RCPackage leavePackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_LEAVE); leavePackage.WriteString(0, "Guest left DSS!"); leavePackage.WriteByteArray(1, new byte[0] { }); this.guestRoot.Lobby.SendControlPackage(leavePackage); /// Stop the timeout clocks and exit from the event-loop. StopTimeouts(); this.guestRoot.EventQueue.ExitEventLoop(); }
/// <summary> /// Sends a commit for this.nextNextRound to the lobby. /// </summary> /// <remarks> /// Call this function only if this.currentRound has been finished. /// </remarks> private void SendCommit() { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } if (this.currentRound.StepNextFrame()) { throw new DssException("DssSimulationMgr.SendCommit() denied!"); } if (!this.hostLeft) { byte[] stateHash = this.root.SimulatorIface.StateHash; int highestAPT = this.HighestAPT; /// Generate a ticket for the commit. int ticket = -1; do { ticket = RandomService.DefaultGenerator.Next(); } while (this.commitAwMonitors.ContainsKey(ticket)); /// Create the commit package. RCPackage commitPackage = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_COMMIT); commitPackage.WriteShort(0, (short)this.aftCalculator.Average); commitPackage.WriteShort(1, (short)highestAPT); /// The highest measured APT commitPackage.WriteInt(2, this.nextNextRound.RoundIndex); /// Round index of the commit commitPackage.WriteInt(3, ticket); /// Commit answer ticket commitPackage.WriteByteArray(4, stateHash != null ? stateHash : new byte[0] { }); /// State-hash value /// Send the commit package to the lobby. this.root.LobbyIface.SendPackage(commitPackage); RegisterCommitMonitor(ticket); TraceManager.WriteAllTrace(string.Format("Self commit round {0}", this.nextNextRound.RoundIndex), DssTraceFilters.SIMULATION_INFO); if (!this.nextNextRound.Commit(this.root.IdOfThisPeer, this.aftCalculator.Average, highestAPT, stateHash)) { SimulationStageError(string.Format("Commit of round-{0} failed!", this.nextNextRound.RoundIndex), new byte[0] { }); } } }
/// <summary> /// Creates the package that contains the description of the terrain objects of the given map. /// </summary> /// <param name="map">Reference to the map.</param> /// <returns>The data package that contains the description of the terrain objects of the given map.</returns> private RCPackage CreateTerrainObjListPackage(IMapAccess map) { RCPackage terrainObjList = RCPackage.CreateCustomDataPackage(MapFileFormat.TERRAINOBJ_LIST); /// Create the terrain object type index table. List <string> terrainObjTypeList = new List <string>(); Dictionary <ITerrainObjectType, int> terrainObjTypeIndexTable = new Dictionary <ITerrainObjectType, int>(); int terrainObjTypeIndex = 0; foreach (ITerrainObjectType terrainObjType in map.Tileset.TerrainObjectTypes) { terrainObjTypeList.Add(terrainObjType.Name); terrainObjTypeIndexTable.Add(terrainObjType, terrainObjTypeIndex); terrainObjTypeIndex++; } terrainObjList.WriteStringArray(0, terrainObjTypeList.ToArray()); /// Create the packages of the terrain objects. List <RCPackage> terrainObjPackages = new List <RCPackage>(); int terrainObjInfoLength = 0; foreach (ITerrainObject terrainObj in map.TerrainObjects) { RCPackage terrainObjPackage = RCPackage.CreateCustomDataPackage(MapFileFormat.TERRAINOBJ); terrainObjPackage.WriteShort(0, (short)terrainObj.MapCoords.X); terrainObjPackage.WriteShort(1, (short)terrainObj.MapCoords.Y); terrainObjPackage.WriteByte(2, (byte)terrainObjTypeIndexTable[terrainObj.Type]); terrainObjPackages.Add(terrainObjPackage); terrainObjInfoLength += terrainObjPackage.PackageLength; } /// Write the terrain object packages into the final package byte[] terrainObjInfoBytes = new byte[terrainObjInfoLength]; int offset = 0; foreach (RCPackage terrainObjPackage in terrainObjPackages) { offset += terrainObjPackage.WritePackageToBuffer(terrainObjInfoBytes, offset); } terrainObjList.WriteByteArray(1, terrainObjInfoBytes); return(terrainObjList); }
/// <summary> /// Sends a DSS_LEAVE message to the lobby and leaves the DSS. /// </summary> private void SendLeaveMessage() { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } StopCommitTimeoutClock(); StopSimulationFrameClock(); UnregisterAllCommitMonitors(); if (!this.hostLeft) { RCPackage leaveMessage = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_LEAVE); leaveMessage.WriteString(0, "Leave DSS during simulation stage!"); leaveMessage.WriteByteArray(1, new byte[0] { }); this.root.LobbyIface.SendPackage(leaveMessage); } }
/// <summary> /// Internal function to send a DSS_CTRL_DROP_GUEST message to the other side of this session. /// </summary> public void DropGuest() { if (this.sm.CurrentState == this.SendingSetupStepRQ) { RCPackage dropGuestMsg = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_DROP_GUEST); dropGuestMsg.WriteString(0, "Dropped from the DSS by the host."); dropGuestMsg.WriteByteArray(1, new byte[0] { }); /// Send the DSS_CTRL_DROP_GUEST message to the guest and close the line immediately this.manager.HostRoot.Lobby.SendControlPackage(dropGuestMsg, this.channel.Index + 1); this.manager.HostRoot.Lobby.CloseLine(this.channel.Index + 1); /// Move the session back to inactive state. this.SendingSetupStepRQ_Inactive.Fire(); } else { throw new DssException("Illegal call to DssHostSessionSM.DropGuest"); } }
/// <summary> /// Internal function to timestamp and send the given commands to the lobby. /// </summary> /// <param name="cmds">The list of the commands to send.</param> private void SendCommandsToLobby(RCPackage[] cmds) { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } if (cmds != null && cmds.Length != 0) { /// Compute the length of the buffer needed for sending the commands as a byte sequence int bufferLength = 0; for (int i = 0; i < cmds.Length; i++) { if (cmds[i] != null && cmds[i].IsCommitted && cmds[i].PackageType == RCPackageType.CUSTOM_DATA_PACKAGE && !DssRoot.IsInternalFormat(cmds[i].PackageFormat)) { bufferLength += cmds[i].PackageLength; } else { throw new DssException("Unexpected command from the client module!"); } } /// Create the buffer and write the commands into this buffer byte[] cmdBuffer = new byte[bufferLength]; int offset = 0; for (int i = 0; i < cmds.Length; i++) { offset += cmds[i].WritePackageToBuffer(cmdBuffer, offset); } /// Create the DSS_COMMAND package and send it to the lobby RCPackage cmdPackage = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_COMMAND); cmdPackage.WriteInt(0, this.nextNextRound.RoundIndex); /// Round index of the command cmdPackage.WriteInt(1, this.currentRound.CurrentFrameIndex); /// Frame index of the command cmdPackage.WriteByteArray(2, cmdBuffer); /// The command list this.root.LobbyIface.SendPackage(cmdPackage); /// Send the command package to the lobby } }
/// <summary> /// Starts the disconnection procedure of this connection. /// </summary> /// <returns>True if the disconnection procedure has been successfully started, false otherwise.</returns> /// <remarks> /// If this function returns false that means that an underlying network error occured and the connection /// has been shutdown immediately. /// </remarks> public bool BeginDisconnect() { if (this.connectionState == LobbyConnectionState.Connected) { /// Send a disconnect indicator message to the other side. RCPackage disconnectIndicator = RCPackage.CreateNetworkControlPackage(Network.FORMAT_DISCONNECT_INDICATOR); disconnectIndicator.WriteString(0, string.Empty); disconnectIndicator.WriteByteArray(1, new byte[0] { }); if (!SendPackage(disconnectIndicator)) { /// Shutdown the connection immediately in case of any error. Shutdown(); return(false); } this.connectionState = LobbyConnectionState.Disconnecting; this.disconnectAckMsgTimer.Start(); return(true); } else { throw new NetworkingSystemException("Unexpected call to LobbyConnection.BeginDisconnect()!"); } }
/// <summary> /// Process all incoming messages arrived from all connections. /// </summary> private void ProcessIncomingMessages() { for (int i = 0; i < this.connections.Length; i++) { if (this.connections[i].ConnectionState == LobbyConnectionState.Connected) { /// The connection is in connected state --> it's incoming messages will be processed by the server List <RCPackage> incomingPackages = new List <RCPackage>(); if (this.connections[i].ReceiveIncomingPackages(ref incomingPackages)) { /// Process the incoming messages foreach (RCPackage package in incomingPackages) { if (package.PackageType == RCPackageType.NETWORK_CUSTOM_PACKAGE) { /// This is a custom message, forward it to every other clients package.Sender = i + 1; for (int j = 0; j < this.connections.Length; j++) { if (i != j && this.connections[j].ConnectionState == LobbyConnectionState.Connected) { if (!this.connections[j].SendPackage(package)) { /// Unable to forward the message to a client --> Shutdown the connection this.connections[j].Shutdown(); this.connections[j].LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); } } } /// Notify the listener object about the arrived package this.listener.PackageArrived(package, i + 1); } else if (package.PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && package.PackageFormat.ID == Network.FORMAT_DEDICATED_MESSAGE) { /// This is a dedicated message, forward only to the targets byte[] targets = package.ReadByteArray(0); /// List of the targets byte[] theMessageBytes = package.ReadByteArray(1); /// The embedded message int parsedBytes = 0; RCPackage theMessage = RCPackage.Parse(theMessageBytes, 0, theMessageBytes.Length, out parsedBytes); if (theMessage != null && theMessage.IsCommitted && theMessage.PackageType == RCPackageType.NETWORK_CUSTOM_PACKAGE) { /// The embedded message is OK --> forward it to the dedicated targets theMessage.Sender = i + 1; for (int j = 0; j < targets.Length; j++) { int target = targets[j]; if (target == 0) { /// This server is the target this.listener.PackageArrived(theMessage, i + 1); } else if (target - 1 >= 0 && target - 1 < this.connections.Length && target - 1 != i) { /// Another client is the target --> forward the message to it LobbyConnection targetConn = this.connections[target - 1]; if (targetConn.ConnectionState == LobbyConnectionState.Connected) { if (!targetConn.SendPackage(theMessage)) { /// Unable to forward the message to a target --> Shutdown the connection targetConn.Shutdown(); targetConn.LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); } } } } } else { /// The embedded message has unexpected format --> Shutdown the connection this.connections[i].Shutdown(); this.connections[i].LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); break; /// Stop processing the messages of the closed connection } } else if (this.connections[i].ConnectionState == LobbyConnectionState.Connected && package.PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && package.PackageFormat.ID == Network.FORMAT_DISCONNECT_INDICATOR) { /// The client at the other side of the connection wants to disconnect. /// Acknowledge this request and shutdown the connection. RCPackage disconnAck = RCPackage.CreateNetworkControlPackage(Network.FORMAT_DISCONNECT_ACK); disconnAck.WriteString(0, string.Empty); disconnAck.WriteByteArray(1, new byte[0] { }); this.connections[i].SendPackage(disconnAck); this.connections[i].Shutdown(); this.connections[i].LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); break; /// Stop processing the messages of the closed connection } else if (package.PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && package.PackageFormat.ID != Network.FORMAT_DEDICATED_MESSAGE && package.PackageFormat.ID != Network.FORMAT_DISCONNECT_ACK && package.PackageFormat.ID != Network.FORMAT_DISCONNECT_INDICATOR && package.PackageFormat.ID != Network.FORMAT_LOBBY_INFO && package.PackageFormat.ID != Network.FORMAT_LOBBY_INFO_VANISHED && package.PackageFormat.ID != Network.FORMAT_LOBBY_LINE_STATE_REPORT) { /// Custom internal message from a client --> notify the listener this.listener.ControlPackageArrived(package, i + 1); } else { /// Unexpected message from the current connection this.connections[i].Shutdown(); this.connections[i].LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); break; /// Stop processing the messages of the closed connection } } /// end-foreach (RCPackage package in incomingPackages) } else { /// In case of receive error, we shutdown the connection. this.connections[i].Shutdown(); this.connections[i].LineState = LobbyLineState.Opened; /// Keep it opened for other clients. SendLineStateReports(); } } else if (this.connections[i].ConnectionState == LobbyConnectionState.Disconnecting) { /// The connection is about to disconnect --> incoming messages will be handled by the connection itself if (this.connections[i].ContinueDisconnect()) { /// This connection remains closed because disconnection is initiated by the server. SendLineStateReports(); } } } /// end-for (int i = 0; i < this.connections.Length; i++) }
/// <summary> /// This function is called when a control package arrives at setup stage. /// </summary> /// <param name="package">The arrived control package.</param> public void SetupStageCtrlPackage(RCPackage package) { bool error = false; if (this.sm.CurrentState == this.WaitingConnectionRQ) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_REQUEST) { RCPackage rejPackage = null; int otherMajor = package.ReadInt(0); int otherMinor = package.ReadInt(1); int otherBuild = package.ReadInt(2); int otherRevision = package.ReadInt(3); if (otherMajor >= 0 && otherMinor >= 0 && otherBuild >= 0 && otherRevision >= 0) { Version otherVer = new Version(otherMajor, otherMinor, otherBuild, otherRevision); if (DssRoot.IsCompatibleVersion(otherVer)) { /// We send back a DSS_CTRL_CONN_ACK package. RCPackage ackPackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_CONN_ACK); ackPackage.WriteInt(0, DssRoot.APPLICATION_VERSION.Major); ackPackage.WriteInt(1, DssRoot.APPLICATION_VERSION.Minor); ackPackage.WriteInt(2, DssRoot.APPLICATION_VERSION.Build); ackPackage.WriteInt(3, DssRoot.APPLICATION_VERSION.Revision); this.manager.HostRoot.Lobby.SendControlPackage(ackPackage, this.channel.Index + 1); this.WaitingConnectionRQ_SendingSetupStepRQ.Fire(); } else { /// We send back a DSS_CTRL_CONN_REJECT package rejPackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_CONN_REJECT); string reason = string.Format("Incompatible with host version: {0} (RC.DssServices)", DssRoot.APPLICATION_VERSION.ToString()); rejPackage.WriteString(0, reason); rejPackage.WriteByteArray(1, new byte[0]); } } else { /// We create a DSS_CTRL_CONN_REJECT package rejPackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_CONN_REJECT); rejPackage.WriteString(0, "Unable to parse version information!"); rejPackage.WriteByteArray(1, new byte[0]); } /// We send back a DSS_CTRL_CONN_REJECT package if necessary. if (rejPackage != null && rejPackage.IsCommitted) { this.manager.HostRoot.Lobby.SendControlPackage(rejPackage, this.channel.Index + 1); error = true; } } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_REQUEST) } /// end-if (this.sm.CurrentState == this.WaitingConnectionRQ) else if (this.sm.CurrentState == this.WaitingSetupStepAW) { if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) { string leaveReason = package.ReadString(0); string trcMsg = string.Format("Guest-{0} has left the DSS. Reason: {1}", this.channel.Index, leaveReason.Length != 0 ? leaveReason : "-"); TraceManager.WriteAllTrace(trcMsg, DssTraceFilters.SETUP_STAGE_INFO); this.WaitingSetupStepAW_Inactive.Fire(); this.channel.GuestLeaveSetupStage(); } /// end-if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) else { SetupStep currentStep = this.manager.HostRoot.GetStep(this.channel.Index); currentStep.IncomingPackage(package); if (currentStep.State == SetupStepState.READY) { /// Setup step answer arrived. this.WaitingSetupStepAW_SendingSetupStepRQ.Fire(); } else if (currentStep.State == SetupStepState.ERROR) { /// Setup step answer error. error = true; } else { /// Setup step answer not finished yet, more packages to wait. } } } /// end-if (this.sm.CurrentState == this.WaitingSetupStepAW) if (error) { /// Go to error state if the package cannot be handled until now. SetupStageError(); this.channel.SetupStageError(); return; } }
/// <summary> /// Resets the state of the DssSimulationMgr. Call this function every time you start a simulation stage. /// </summary> /// <param name="opFlags"> /// The current operator flag array. See the comment at this.operatorFlags for more information. /// </param> public void Reset(bool[] opFlags) { if (opFlags == null) { throw new ArgumentNullException("opFlags"); } if (opFlags.Length != this.root.OpCount) { throw new ArgumentException("Array length mismatch.", "opFlags"); } this.aftCalculator = new AverageCalculator(DssConstants.AVG_CALC_VECTOR_LENGTH, DssConstants.INITIAL_AFT); this.aptCalculators = new AverageCalculator[this.root.OpCount]; for (int i = 0; i < this.aptCalculators.Length; i++) { this.aptCalculators[i] = new AverageCalculator(DssConstants.AVG_CALC_VECTOR_LENGTH, DssConstants.INITIAL_APT); } if (this.commitAwTimeouts != null) { foreach (KeyValuePair <AlarmClock, CommitMonitor> item in this.commitAwTimeouts) { item.Key.Cancel(); } } this.commitAwMonitors = new Dictionary <int, CommitMonitor>(); this.commitAwTimeouts = new Dictionary <AlarmClock, CommitMonitor>(); StopCommitTimeoutClock(); StopSimulationFrameClock(); this.operatorFlags = opFlags; this.currentRound = new SimulationRound(this.operatorFlags); this.currentRound.Reset(true, 0); this.currentRound.ComputeSpeedControl(); this.nextRound = new SimulationRound(this.operatorFlags); this.nextRound.Reset(false, 1); this.nextNextRound = new SimulationRound(this.operatorFlags); this.nextNextRound.Reset(false, 2); this.nextNextNextRound = new SimulationRound(this.operatorFlags); this.nextNextNextRound.Reset(false, 3); this.waitingForCommit = false; this.initialized = true; this.hostLeft = false; int ticket = RandomService.DefaultGenerator.Next(); byte[] stateHash = this.root.SimulatorIface.StateHash; int highestAPT = this.HighestAPT; this.commitTimeoutClock = this.root.AlarmClkMgr.SetAlarmClock(DssRoot.Time + DssConstants.COMMIT_TIMEOUT, this.CommitTimeout); /// Create the first commit package. RCPackage commitPackage = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_COMMIT); commitPackage.WriteShort(0, (short)this.aftCalculator.Average); commitPackage.WriteShort(1, (short)highestAPT); /// The highest measured APT commitPackage.WriteInt(2, 1); /// Round index of the commit is 1 (next round is committed) commitPackage.WriteInt(3, ticket); /// Commit answer ticket commitPackage.WriteByteArray(4, stateHash != null ? stateHash : new byte[0] { }); /// State-hash value /// Send the commit package to the lobby. this.root.LobbyIface.SendPackage(commitPackage); RegisterCommitMonitor(ticket); TraceManager.WriteAllTrace(string.Format("Self commit round {0}", this.nextRound.RoundIndex), DssTraceFilters.SIMULATION_INFO); if (!this.nextRound.Commit(this.root.IdOfThisPeer, this.aftCalculator.Average, highestAPT, stateHash)) { SimulationStageError(string.Format("Commit of round-{0} failed!", this.nextRound.RoundIndex), new byte[0] { }); } }
/// <summary> /// The function sends every outgoing messages. /// </summary> /// <returns>False if the connection manager thread has to stop, true otherwise.</returns> private bool SendOutgoingMessages() { bool errorOccured = false; lock (this.outgoingPackages) { if (this.outgoingPackages.Count == this.outgoingPackageTargets.Count) { if (this.connection.ConnectionState == LobbyConnectionState.Connected) { for (int i = 0; i < this.outgoingPackages.Count; i++) { int[] targets = this.outgoingPackageTargets[i]; if (targets != null) { /// This is a dedicated message --> Send it as an embedded message. /// First collect the targets. List <byte> targetBytesList = new List <byte>(); for (int j = 0; j < targets.Length; j++) { if (targets[j] != this.clientID && targets[j] >= 0 && targets[j] < this.memberCount) { targetBytesList.Add((byte)targets[j]); } } /// Then create the dedicated message and send it to the server. byte[] targetBytes = targetBytesList.ToArray(); if (targetBytes != null && targetBytes.Length > 0) { byte[] packageBytes = new byte[this.outgoingPackages[i].PackageLength]; this.outgoingPackages[i].WritePackageToBuffer(packageBytes, 0); RCPackage dedicatedPackage = RCPackage.CreateNetworkControlPackage(Network.FORMAT_DEDICATED_MESSAGE); dedicatedPackage.WriteByteArray(0, targetBytes); /// The targets of the message. dedicatedPackage.WriteByteArray(1, packageBytes); /// The message itself. if (!this.connection.SendPackage(dedicatedPackage)) { /// Error when try to send --> immediate shutdown. this.connection.Shutdown(); //if (this.Disposed != null) { this.Disposed(this); } //this.listener.LobbyLost(); errorOccured = true; break; } } } else { /// This is a simple message. if (!this.connection.SendPackage(this.outgoingPackages[i])) { /// Error when try to send --> immediate shutdown. this.connection.Shutdown(); //if (this.Disposed != null) { this.Disposed(this); } //this.listener.LobbyLost(); errorOccured = true; break; } } } } /// Clear the FIFO this.outgoingPackages.Clear(); this.outgoingPackageTargets.Clear(); /// Outgoing messages sent successfully. ///return true; } else { throw new NetworkingSystemException("Inconsistence in the outgoing package FIFO!"); } } if (errorOccured) { /// An error happened during the send. if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } else { /// Outgoing messages sent successfully. return(true); } }
/// <summary> /// This function reads and processes every incoming message arriving from the server. /// </summary> /// <returns>False, if the connection manager thread has to stop, true otherwise.</returns> private bool ProcessIncomingMessages() { List <RCPackage> incomingPackages = new List <RCPackage>(); if (this.connection.ReceiveIncomingPackages(ref incomingPackages)) { foreach (RCPackage package in incomingPackages) { if (package.PackageType == RCPackageType.NETWORK_CUSTOM_PACKAGE && package.Sender >= 0 && package.Sender < this.memberCount && package.Sender != this.clientID) { /// Custom message from a member --> notify the listener. this.listener.PackageArrived(package, package.Sender); } else if (package.PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && package.PackageFormat.ID == Network.FORMAT_LOBBY_LINE_STATE_REPORT) { /// Line state report from the server. short clientID = package.ReadShort(0); byte[] lineStateBytes = package.ReadByteArray(1); LobbyLineState[] lineStates = new LobbyLineState[lineStateBytes.Length]; bool lineStatesOK = true; for (int i = 0; i < lineStateBytes.Length; i++) { if (lineStateBytes[i] == (byte)LobbyLineState.Closed || lineStateBytes[i] == (byte)LobbyLineState.Engaged || lineStateBytes[i] == (byte)LobbyLineState.Opened) { lineStates[i] = (LobbyLineState)lineStateBytes[i]; } else { lineStatesOK = false; } } lineStatesOK = lineStatesOK && (clientID == this.clientID) && (lineStateBytes.Length == this.memberCount); if (!lineStatesOK) { /// Line state report error --> shutdown. this.connection.Shutdown(); if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } else { /// Line state report arrived --> notify the listener this.listener.LineStateReport(clientID, lineStates); ///return true; } } else if (package.PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && package.PackageFormat.ID == Network.FORMAT_DISCONNECT_INDICATOR) { /// Disconnection indicator from the server. RCPackage disconnectAck = RCPackage.CreateNetworkControlPackage(Network.FORMAT_DISCONNECT_ACK); disconnectAck.WriteString(0, string.Empty); disconnectAck.WriteByteArray(1, new byte[0] { }); this.connection.SendPackage(disconnectAck); this.connection.Shutdown(); if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } else if (package.PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && package.PackageFormat.ID != Network.FORMAT_DEDICATED_MESSAGE && package.PackageFormat.ID != Network.FORMAT_DISCONNECT_ACK && package.PackageFormat.ID != Network.FORMAT_DISCONNECT_INDICATOR && package.PackageFormat.ID != Network.FORMAT_LOBBY_INFO && package.PackageFormat.ID != Network.FORMAT_LOBBY_INFO_VANISHED && package.PackageFormat.ID != Network.FORMAT_LOBBY_LINE_STATE_REPORT) { /// Custom internal message from the server --> notify the listener TraceManager.WriteAllTrace(string.Format("Incoming package: {0}", package.ToString()), NetworkingSystemTraceFilters.INFO); this.listener.ControlPackageArrived(package); } else { /// Unexpected package format and type --> immediate shutdown this.connection.Shutdown(); if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } } /// end-foreach (RCPackage package in incomingPackages) /// Incoming packages has been processed. return(true); } else { /// Receive error --> immediate shutdown this.connection.Shutdown(); if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } }
public static RCPackage GenerateRandomPackage() { int rndType = rnd.Next(0, 3); int rndFormat = rnd.Next(0, 3); RCPackage retPack = null; if (rndType == 0) { retPack = RCPackage.CreateNetworkPingPackage(); return(retPack); } else if (rndType == 1) { retPack = RCPackage.CreateCustomDataPackage(rndFormat); } else if (rndType == 2) { retPack = RCPackage.CreateNetworkCustomPackage(rndFormat); } RCPackageFormat format = RCPackageFormat.GetPackageFormat(rndFormat); for (int i = 0; i < format.NumOfFields; i++) { RCPackageFieldType datatype = format.GetFieldType(i); if (datatype == RCPackageFieldType.BYTE) { retPack.WriteByte(i, (byte)rnd.Next(byte.MinValue, byte.MaxValue)); } else if (datatype == RCPackageFieldType.SHORT) { retPack.WriteShort(i, (short)rnd.Next(short.MinValue, short.MaxValue)); } else if (datatype == RCPackageFieldType.INT) { retPack.WriteInt(i, (int)rnd.Next(int.MinValue, int.MaxValue)); } else if (datatype == RCPackageFieldType.LONG) { retPack.WriteLong(i, (long)rnd.Next(int.MinValue, int.MaxValue)); } else if (datatype == RCPackageFieldType.STRING) { int strIdx = rnd.Next(0, 10); retPack.WriteString(i, strCollection[strIdx]); } else if (datatype == RCPackageFieldType.BYTE_ARRAY) { int arrLen = rnd.Next(0, 10); byte[] arr = new byte[arrLen]; rnd.NextBytes(arr); retPack.WriteByteArray(i, arr); } else if (datatype == RCPackageFieldType.SHORT_ARRAY) { int arrLen = rnd.Next(0, 10); short[] arr = new short[arrLen]; for (int j = 0; j < arrLen; ++j) { arr[j] = (short)rnd.Next(short.MinValue, short.MaxValue); } retPack.WriteShortArray(i, arr); } else if (datatype == RCPackageFieldType.INT_ARRAY) { int arrLen = rnd.Next(0, 10); int[] arr = new int[arrLen]; for (int j = 0; j < arrLen; ++j) { arr[j] = (int)rnd.Next(int.MinValue, int.MaxValue); } retPack.WriteIntArray(i, arr); } else if (datatype == RCPackageFieldType.LONG_ARRAY) { int arrLen = rnd.Next(0, 10); long[] arr = new long[arrLen]; for (int j = 0; j < arrLen; ++j) { arr[j] = (long)rnd.Next(int.MinValue, int.MaxValue); } retPack.WriteLongArray(i, arr); } else if (datatype == RCPackageFieldType.STRING_ARRAY) { int arrLen = rnd.Next(0, 10); string[] arr = new string[arrLen]; for (int j = 0; j < arrLen; ++j) { int strIdx = rnd.Next(0, 10); arr[j] = strCollection[strIdx]; } retPack.WriteStringArray(i, arr); } else { throw new NetworkingSystemException("Unknown datatype"); } } return(retPack); }
/// <summary> /// Processes the channel events and incoming answers arrived from the guests. /// </summary> private void ProcessGuestChannels(IDssGuestChannel[] channelsToGuests) { RCSet <int> newGuests = new RCSet <int>(); /// First collect the new guests for (int i = 0; i < this.previousChannelStates.Length; i++) { if (this.previousChannelStates[i] != channelsToGuests[i].ChannelState) { /// The state of a channel has changed this.previousChannelStates[i] = channelsToGuests[i].ChannelState; if (channelsToGuests[i].ChannelState == DssChannelState.CHANNEL_OPENED) { this.uiCallMarshal.SetGuestControlStatus(i, GuestControlStatus.HostSideOpened); } else if (channelsToGuests[i].ChannelState == DssChannelState.CHANNEL_CLOSED) { this.uiCallMarshal.SetGuestControlStatus(i, GuestControlStatus.HostSideClosed); } else if (channelsToGuests[i].ChannelState == DssChannelState.GUEST_CONNECTED) { this.uiCallMarshal.SetGuestControlStatus(i, GuestControlStatus.HostSideEngaged); this.simulator.GetPlayer(i + 1).Activate(); newGuests.Add(i); } } } /// Then process the answers of any other guests. for (int i = 0; i < this.previousChannelStates.Length; i++) { if (!newGuests.Contains(i) && channelsToGuests[i].ChannelState == DssChannelState.GUEST_CONNECTED) { /// If a guest is connected to this channel, process it's answer. RCPackage[] answerFromGuest = channelsToGuests[i].AnswerFromGuest; for (int j = 0; j < answerFromGuest.Length; j++) { if (answerFromGuest[j].PackageFormat.ID == TestClientMessages.COLOR_CHANGE_REQUEST) { /// Color change request arrived from the guest. PlayerColor newColor = (PlayerColor)answerFromGuest[j].ReadByte(0); this.simulator.GetPlayer(i + 1).Color = newColor; /// Notify the other connected guests. for (int k = 0; k < channelsToGuests.Length; k++) { if (!newGuests.Contains(k) && i != k && channelsToGuests[k].ChannelState == DssChannelState.GUEST_CONNECTED) { RCPackage colorChgNotif = RCPackage.CreateNetworkControlPackage(TestClientMessages.COLOR_CHANGE_NOTIFICATION); colorChgNotif.WriteInt(0, i + 1); colorChgNotif.WriteByte(1, (byte)newColor); this.rqs[k].Add(colorChgNotif); } } /// Notify the UI this.uiCallMarshal.SelectNewGuestColor(i, newColor); break; /// Ignore the remaining messages } } } } /// Send a reset message to the new guests foreach (int newGuestIdx in newGuests) { byte[] colors = new byte[this.simulator.MaxNumOfPlayers]; int[] xCoords = new int[this.simulator.MaxNumOfPlayers]; int[] yCoords = new int[this.simulator.MaxNumOfPlayers]; for (int j = 0; j < colors.Length; j++) { colors[j] = (byte)this.simulator.GetPlayer(j).Color; xCoords[j] = this.simulator.GetPlayer(j).Position.X; yCoords[j] = this.simulator.GetPlayer(j).Position.Y; } RCPackage reset = RCPackage.CreateNetworkControlPackage(TestClientMessages.RESET); reset.WriteByteArray(0, colors); reset.WriteIntArray(1, xCoords); reset.WriteIntArray(2, yCoords); this.rqs[newGuestIdx].Add(reset); } }
/// <summary> /// Starts the simulation. /// </summary> private void StartSimulation() { /// Collect opFlags and channel state informations. byte[] chStates = new byte[this.channelProxies.Length]; bool[] opFlags = new bool[this.hostRoot.OpCount]; opFlags[0] = true; for (int i = 0; i < chStates.Length; i++) { if (this.channels[i].CurrentState == this.channels[i].Opened) { /// If the channel is opened then it will be closed. //this.channels[i].CloseChannel(); this.channels[i].PermanentlyClose(); chStates[i] = (byte)DssChannelState.CHANNEL_CLOSED;//CHANNEL_OPENED; opFlags[i + 1] = false; } else if (this.channels[i].CurrentState == this.channels[i].Closed) { /// If the channel is closed then it will remain closed. this.channels[i].PermanentlyClose(); chStates[i] = (byte)DssChannelState.CHANNEL_CLOSED; opFlags[i + 1] = false; } else if (this.channels[i].CurrentState == this.channels[i].Engaged) { /// If the channel is engaged then we have to check whether the guest will be dropped or not. if (this.channelProxies[i].TaskToPerform == DssChannelTask.DROP_AND_OPEN || this.channelProxies[i].TaskToPerform == DssChannelTask.DROP_AND_CLOSE) { /// Close channel permanently. this.channels[i].PermanentlyClose(); this.hostRoot.GuestLeftDss(i); chStates[i] = (byte)DssChannelState.CHANNEL_CLOSED; opFlags[i + 1] = false; } else { /// Don't drop guest, it will participate in the simulation stage. chStates[i] = (byte)DssChannelState.GUEST_CONNECTED; opFlags[i + 1] = true; } } else { throw new DssException("Unexpected channel state!"); } } int[] leftList = null; int[] lostList = null; this.hostRoot.GetGuestEvents(out leftList, out lostList); RCPackage startSimMsg = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_START_SIMULATION); startSimMsg.WriteIntArray(0, leftList); startSimMsg.WriteIntArray(1, lostList); startSimMsg.WriteByteArray(2, chStates); /// Send the DSS_CTRL_START_SIMULATION message to every engaged channels for (int i = 0; i < this.sessions.Length; i++) { if (opFlags[i + 1]) { /// Push the corresponding session and channel to the Simulating state this.channels[i].StartSimulation(); this.sessions[i].StartSimulation(); /// Send the start message to the corresponding guest this.hostRoot.Lobby.SendControlPackage(startSimMsg, i + 1); } } /// Reset the simulation manager and ask it to execute the next frame immediately. this.hostRoot.SimulationMgr.Reset(opFlags); this.hostRoot.SimulationMgr.SetNextFrameExecutionTime(DssRoot.Time); /// And finally go to the SimulationStage. this.SendingSetupStepRQs_SimulationStage.Fire(); }