/// <summary> /// Initializes the isometric tiles of the map structure. /// </summary> /// <param name="isotileListPackage">The package that contains the isometric tile informations.</param> private void LoadIsoTiles(RCPackage isotileListPackage) { string[] terrainIndexTable = isotileListPackage.ReadStringArray(0); byte[] isotileInfoBytes = isotileListPackage.ReadByteArray(1); int offset = 0; while (offset < isotileInfoBytes.Length) { int parsedBytes; RCPackage package = RCPackage.Parse(isotileInfoBytes, offset, isotileInfoBytes.Length - offset, out parsedBytes); if (package == null || !package.IsCommitted) { throw new MapException("Syntax error!"); } offset += parsedBytes; if (package.PackageFormat.ID == MapFileFormat.ISOTILE) { RCIntVector quadCoords = new RCIntVector(package.ReadShort(0), package.ReadShort(1)); TerrainCombination terrainCombo = (TerrainCombination)package.ReadByte(4); string terrainA = terrainIndexTable[package.ReadByte(2)]; string terrainB = terrainCombo != TerrainCombination.Simple ? terrainIndexTable[package.ReadByte(3)] : null; int variantIdx = package.ReadByte(5); this.mapStructure.InitIsoTile(quadCoords, terrainCombo == TerrainCombination.Simple ? this.mapStructure.Tileset.GetIsoTileType(terrainA) : this.mapStructure.Tileset.GetIsoTileType(terrainA, terrainB, terrainCombo), variantIdx); } } }
/// <summary> /// Called when a NETWORK_CUSTOM_PACKAGE has arrived from the lobby. /// </summary> /// <remarks>Must be implemented at both server and client side.</remarks> public virtual void PackageArrivedHdl(int timestamp, RCPackage package, int senderID) { if (IsSimulationRunning_i()) { /// Package arrived during simulation stage must be handled. if (package.PackageFormat.ID == DssRoot.DSS_COMMAND) { if (!CommandPackageArrived(package, senderID)) { this.root.SimulationMgr.SimulationStageError(string.Format("Processing incoming DSS_COMMAND package failed. Sender: {0}. Package: {1}", senderID, package.ToString()), new byte[] { }); } } else if (package.PackageFormat.ID == DssRoot.DSS_COMMIT) { if (!this.root.SimulationMgr.RegisterCommit(package, senderID)) { this.root.SimulationMgr.SimulationStageError(string.Format("Processing incoming DSS_COMMIT package failed. Sender: {0}. Package: {1}", senderID, package.ToString()), new byte[] { }); } } else if (package.PackageFormat.ID == DssRoot.DSS_COMMIT_ANSWER) { if (!this.root.SimulationMgr.RegisterCommitAnswer(package, senderID)) { this.root.SimulationMgr.SimulationStageError(string.Format("Processing incoming DSS_COMMIT_ANSWER package failed. Sender: {0}. Package: {1}", senderID, package.ToString()), new byte[] { }); } } else if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) { if (this.root.SimulationMgr.RegisterLeaveMessage(package, senderID)) { this.root.OperatorLeftSimulationStage(senderID); } else { this.root.SimulationMgr.SimulationStageError(string.Format("Processing incoming DSS_LEAVE package failed. Sender: {0}. Package: {1}", senderID, package.ToString()), new byte[] { }); } } else if (package.PackageFormat.ID == DssRoot.DSS_SIM_ERROR) { string errorDescr = package.ReadString(0); byte[] customData = package.ReadByteArray(1); this.root.SimulationMgr.SimulationStageErrorReceived(string.Format("DSS_SIM_ERROR received from operator-{0}: {1}", senderID, errorDescr), customData); } else { this.root.SimulationMgr.SimulationStageError(string.Format("Unexpected package arrived from operator-{0}: {1}", senderID, package.ToString()), new byte[] { }); } } else { /// Package arrived during setup stage is not allowed. PackageArrivedDuringSetupStage_i(package, senderID); } }
/// <summary> /// Initializes the terrain objects of the map. /// </summary> /// <param name="terrainObjListPackage">The package that contains the terrain object informations.</param> /// <param name="map">Reference to the map.</param> private void LoadTerrainObjects(RCPackage terrainObjListPackage, IMapAccess map) { /// TODO: Avoid this downcast! MapAccess mapObj = map as MapAccess; if (mapObj == null) { throw new ArgumentException("The given map cannot be handled by the MapEditor!", "map"); } string[] terrainObjIndexTable = terrainObjListPackage.ReadStringArray(0); byte[] terrainObjInfoBytes = terrainObjListPackage.ReadByteArray(1); int offset = 0; while (offset < terrainObjInfoBytes.Length) { int parsedBytes; RCPackage package = RCPackage.Parse(terrainObjInfoBytes, offset, terrainObjInfoBytes.Length - offset, out parsedBytes); if (package == null || !package.IsCommitted) { throw new MapException("Syntax error!"); } offset += parsedBytes; if (package.PackageFormat.ID == MapFileFormat.TERRAINOBJ) { RCIntVector quadCoords = new RCIntVector(package.ReadShort(0), package.ReadShort(1)); ITerrainObjectType terrainObjType = this.mapStructure.Tileset.GetTerrainObjectType(terrainObjIndexTable[package.ReadByte(2)]); /// TODO: Might be better to create the TerrainObject with a factory? ITerrainObject newObj = new TerrainObject(map, terrainObjType, quadCoords); foreach (ICellDataChangeSet changeset in newObj.Type.CellDataChangesets) { changeset.Apply(newObj); } mapObj.AttachTerrainObject(newObj); } } /// Check the constraints of the terrain objects. List <ITerrainObject> terrainObjects = new List <ITerrainObject>(map.TerrainObjects); foreach (ITerrainObject terrainObj in terrainObjects) { mapObj.DetachTerrainObject(terrainObj); if (terrainObj.Type.CheckConstraints(map, terrainObj.MapCoords).Count != 0) { throw new MapException(string.Format("Terrain object at {0} is voilating the tileset constraints!", terrainObj.MapCoords)); } if (terrainObj.Type.CheckTerrainObjectIntersections(map, terrainObj.MapCoords).Count != 0) { throw new MapException(string.Format("Terrain object at {0} intersects other terrain objects!", terrainObj.MapCoords)); } mapObj.AttachTerrainObject(terrainObj); } }
/// <summary> /// Internal function to handle command packages and send them to the simulation manager. /// </summary> /// <param name="commandPackage">The package that contains the commands.</param> /// <param name="senderID">The ID of the sender peer.</param> /// <returns>True in case of success, false otherwise.</returns> private bool CommandPackageArrived(RCPackage commandPackage, int senderID) { if (commandPackage == null) { throw new ArgumentNullException("commandPackage"); } if (!commandPackage.IsCommitted || DssRoot.DSS_COMMAND != commandPackage.PackageFormat.ID) { throw new ArgumentException("commandPackage"); } if (senderID < 0 || senderID >= this.root.OpCount) { throw new ArgumentException("senderID"); } if (senderID == this.root.IdOfThisPeer) { throw new ArgumentException("senderID"); } int roundIdx = commandPackage.ReadInt(0); int frameIdx = commandPackage.ReadInt(1); byte[] cmdBuffer = commandPackage.ReadByteArray(2); if (cmdBuffer.Length == 0) { return(false); } List <RCPackage> cmds = new List <RCPackage>(); int offset = 0; int remainingBytes = cmdBuffer.Length; while (offset < cmdBuffer.Length) { /// Parse and check the next command package. int parsedBytes = 0; RCPackage currPackage = RCPackage.Parse(cmdBuffer, offset, remainingBytes, out parsedBytes); if (currPackage != null && currPackage.IsCommitted) { cmds.Add(currPackage); offset += parsedBytes; remainingBytes -= parsedBytes; } else { /// Uncommitted command package. return(false); } } return(this.root.SimulationMgr.RegisterCommands(cmds.ToArray(), roundIdx, frameIdx, senderID)); }
/// <summary> /// Constructs a LobbyInfo object from an RCPackage. /// </summary> /// <param name="source">The package that contains the LobbyInfo data.</param> /// <returns>The contructed LobbyInfo or null if no LobbyInfo can be constructed from the given RCPackage.</returns> public static LobbyInfo FromRCPackage(RCPackage source) { if (null != source && source.IsCommitted && source.PackageFormat.ID == Network.FORMAT_LOBBY_INFO) { string idStr = source.ReadString(0); Guid id; if (!Guid.TryParse(idStr, out id)) { TraceManager.WriteAllTrace(string.Format("Unable to parse {0} as a GUID!", idStr), NetworkingSystemTraceFilters.INFO); return(null); } byte[] customDataBytes = source.ReadByteArray(3); int parsedBytes = 0; RCPackage customData = null; if (customDataBytes.Length > 0) { RCPackage.Parse(customDataBytes, 0, customDataBytes.Length, out parsedBytes); } if (customDataBytes.Length == 0 || (null != customData && customData.IsCommitted && parsedBytes == customDataBytes.Length)) { LobbyInfo retInfo = new LobbyInfo(id, /// ID source.ReadString(1), /// IPAddress source.ReadInt(2), /// PortNumber (customDataBytes.Length != 0) ? customData : null /// Custom infos about the lobby ); return(retInfo); } else { TraceManager.WriteAllTrace("LobbyInfo.FromRCPackage failed: unexpected CustomData package format!", NetworkingSystemTraceFilters.INFO); return(null); } } else { TraceManager.WriteAllTrace("LobbyInfo.FromRCPackage failed: unexpected package format!", NetworkingSystemTraceFilters.INFO); return(null); } }
/// <summary> /// Registers the given DSS_LEAVE message arrived from the given operator. /// </summary> /// <param name="leaveMsg">The arrived DSS_LEAVE message.</param> /// <param name="senderID">The sender operator.</param> /// <returns>True in case of success, false otherwise.</returns> public bool RegisterLeaveMessage(RCPackage leaveMsg, int senderID) { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } /// If we are currently finishing the simulation --> do nothing if (this.hostLeft) { return(true); } if (leaveMsg == null || !leaveMsg.IsCommitted) { throw new ArgumentException("leaveMsg"); } if (senderID == this.root.IdOfThisPeer) { throw new DssException("Unexpected DSS_LEAVE sender!"); } if (senderID < 0 || senderID >= this.root.OpCount) { throw new ArgumentException("senderID"); } if (leaveMsg.PackageFormat.ID != DssRoot.DSS_LEAVE) { /// Package format error. return(false); } string reason = leaveMsg.ReadString(0); byte[] customData = leaveMsg.ReadByteArray(1); TraceManager.WriteAllTrace(string.Format("DSS_LEAVE: sender={0}, reason= {1}", senderID, reason), DssTraceFilters.SIMULATION_INFO); /// Set the corresponding operator flag to false. this.operatorFlags[senderID] = false; if (senderID > 0) { /// GUEST-leave /// Unregister all CommitMonitors that became unnecessary. List <int> commitAwTicketsToUnreg = new List <int>(); foreach (KeyValuePair <int, CommitMonitor> monitor in this.commitAwMonitors) { monitor.Value.Refresh(); if (monitor.Value.IsCommitAnswered) { commitAwTicketsToUnreg.Add(monitor.Value.Ticket); } } foreach (int ticket in commitAwTicketsToUnreg) { UnregisterCommitMonitor(ticket); } /// Try to register the leave of operator to the nextRound. if (!this.nextRound.Leave(senderID)) { /// If failed, try to register to the nextNextRound. if (!this.nextNextRound.Leave(senderID)) { /// If failed, try to register to the nextNextNextRound. if (!this.nextNextNextRound.Leave(senderID)) { /// Error: unable to register TraceManager.WriteAllTrace(string.Format("Unable to register the leave of operator-{0}.", senderID), DssTraceFilters.SIMULATION_ERROR); return(false); } } } /// Now check whether the next round has become committed with this leave message. if (this.waitingForCommit && this.nextRound.IsCommitted) { TraceManager.WriteAllTrace("WAITING FOR COMMIT END (guest left)", DssTraceFilters.SIMULATION_INFO); /// The missing commit has arrived, so we can continue the simulation this.waitingForCommit = false; /// Compute the scheduled time for the next frame int nextFrameStartTime = this.currentRound.BaseTime + (this.currentRound.CurrentFrameIndex + 1) * this.currentRound.TargetFrameTime; SendCommit(); SwapRounds(); /// Schedule the next frame. SetNextFrameExecutionTime(Math.Max(nextFrameStartTime, DssRoot.Time)); } } else { /// HOST-leave /// Stop the timers UnregisterAllCommitMonitors(); StopCommitTimeoutClock(); if (this.waitingForCommit && this.nextRound.IsCommitted) { /// Can continue with the next round TraceManager.WriteAllTrace("WAITING FOR COMMIT END (host left)", DssTraceFilters.SIMULATION_INFO); /// The missing commit has arrived, so we can continue the simulation this.waitingForCommit = false; /// Compute the scheduled time for the next frame int nextFrameStartTime = this.currentRound.BaseTime + (this.currentRound.CurrentFrameIndex + 1) * this.currentRound.TargetFrameTime; SendCommit(); SwapRounds(); /// Schedule the next frame. SetNextFrameExecutionTime(Math.Max(nextFrameStartTime, DssRoot.Time)); } else if (this.waitingForCommit && !this.nextRound.IsCommitted) { /// Cannot continue with the next round --> notify the client module and exit the event loop NotifyClientAboutLeavingGuests(this.nextRound); NotifyClientAboutLeavingGuests(this.nextNextRound); NotifyClientAboutLeavingGuests(this.nextNextNextRound); this.root.SimulatorIface.HostLeftDssDuringSim(); this.root.EventQueue.ExitEventLoop(); } this.hostLeft = true; } return(true); }
/// <summary> /// Registers the given commit arrived from the given operator. /// </summary> /// <param name="commit">The arrived commit package.</param> /// <param name="senderID">The sender operator.</param> /// <returns>True in case of success, false otherwise.</returns> public bool RegisterCommit(RCPackage commit, int senderID) { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } /// If we are currently finishing the simulation --> do nothing if (this.hostLeft) { return(true); } if (commit == null || !commit.IsCommitted) { throw new ArgumentException("commit"); } if (senderID == this.root.IdOfThisPeer) { throw new DssException("Unexpected commit sender!"); } if (senderID < 0 || senderID >= this.root.OpCount) { throw new ArgumentException("senderID"); } if (commit.PackageFormat.ID != DssRoot.DSS_COMMIT) { /// Package format error. return(false); } short senderAFT = commit.ReadShort(0); short senderAPT = commit.ReadShort(1); int roundIdx = commit.ReadInt(2); int ticket = commit.ReadInt(3); byte[] stateHash = commit.ReadByteArray(4); if (senderAFT < 0) { return(false); } if (senderAPT < 0) { return(false); } if (stateHash == null) { return(false); } bool success = false; if (roundIdx == this.nextRound.RoundIndex) { success = this.nextRound.Commit(senderID, senderAFT, senderAPT, stateHash); if (this.waitingForCommit && this.nextRound.IsCommitted) { TraceManager.WriteAllTrace("WAITING FOR COMMIT END", DssTraceFilters.SIMULATION_INFO); /// The missing commit has arrived, so we can continue the simulation this.waitingForCommit = false; /// Compute the scheduled time for the next frame int nextFrameStartTime = this.currentRound.BaseTime + (this.currentRound.CurrentFrameIndex + 1) * this.currentRound.TargetFrameTime; SendCommit(); SwapRounds(); /// Schedule the next frame. SetNextFrameExecutionTime(Math.Max(nextFrameStartTime, DssRoot.Time)); } } else if (roundIdx == this.nextNextRound.RoundIndex) { success = this.nextNextRound.Commit(senderID, senderAFT, senderAPT, stateHash); } else { TraceManager.WriteAllTrace(string.Format("RoundIdx mismatch! RoundIdx: {0}", roundIdx), DssTraceFilters.SIMULATION_ERROR); } if (success) { /// Create the answer for the commit and send it back to the sender. RCPackage commitAw = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_COMMIT_ANSWER); commitAw.WriteInt(0, ticket); this.root.LobbyIface.SendPackage(commitAw, new int[1] { senderID }); } return(success); }
/// <summary> /// Continues the connection procedure to the server. /// </summary> /// <returns> /// In case of error this function automatically shuts down the connection, notifies the listener and /// returns false. Otherwise it returns true. If the line state report arrives from the server, this /// function automatically notifies the listener. /// </returns> private bool ContinueConnectToTheServer() { RCPackage lineStateReport = null; if (this.connection.ContinueConnectToTheServer(out lineStateReport)) { if (lineStateReport != null) { short clientID = lineStateReport.ReadShort(0); byte[] lineStateBytes = lineStateReport.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 > 0 && clientID < lineStateBytes.Length; if (!lineStatesOK) { /// Line state report error --> shutdown. this.connection.Shutdown(); if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } else { /// Connection procedure finished --> send a line state report to the listener. this.clientID = clientID; this.memberCount = lineStates.Length; this.listener.LineStateReport(clientID, lineStates); return(true); } } /// No error but connection procedure not finished. return(true); } else { /// Connection rejected by the server or an error occured --> shutdown. this.connection.Shutdown(); if (this.Disposed != null) { this.Disposed(this); } this.listener.LobbyLost(); return(false); } }
/// <summary> /// Internal function for parsing a DSS_CTRL_START_SIMULATION package. /// </summary> /// <param name="package">The package to parse.</param> /// <returns>True if the package has been successfully parsed, false otherwise.</returns> private bool ParseStartSimPackage(RCPackage package, out int[] leftList, out int[] lostList, out bool[] opFlags) { /// Read the left-list leftList = package.ReadIntArray(0); for (int i = 0; i < leftList.Length; ++i) { if (leftList[i] < 0) { leftList = null; lostList = null; opFlags = null; return(false); } } /// Read the lost-list lostList = package.ReadIntArray(1); for (int i = 0; i < lostList.Length; ++i) { if (lostList[i] < 0) { leftList = null; lostList = null; opFlags = null; return(false); } } /// Read the channel-state-list byte[] chStateBytes = package.ReadByteArray(2); if (chStateBytes.Length == this.guestRoot.OpCount - 1) { opFlags = new bool[this.guestRoot.OpCount]; opFlags[0] = true; for (int i = 0; i < chStateBytes.Length; ++i) { if (chStateBytes[i] == (byte)DssChannelState.CHANNEL_CLOSED || chStateBytes[i] == (byte)DssChannelState.CHANNEL_OPENED || chStateBytes[i] == (byte)DssChannelState.GUEST_CONNECTED) { opFlags[i + 1] = (chStateBytes[i] == (byte)DssChannelState.GUEST_CONNECTED); } else { /// Error leftList = null; lostList = null; opFlags = null; return(false); } } } /// end-if (chStateBytes != null && chStateBytes.Length != this.guestRoot.OpCount - 1) else { /// Error leftList = null; lostList = null; opFlags = null; return(false); } /// Everything is OK, the start simulation message arrived successfully. return(true); }
/// <summary> /// Call this function if a control package has arrived from the network and you are waiting for a setup step /// answer or request. This class will parse it for you automatically. /// </summary> /// <param name="package">The incoming control package.</param> public void IncomingPackage(RCPackage package) { if (package == null) { throw new ArgumentNullException("package"); } if (!package.IsCommitted) { throw new ArgumentException("Uncommitted package!", "package"); } if (package.PackageType != RCPackageType.NETWORK_CONTROL_PACKAGE) { throw new ArgumentException("Unexpected package type!", "package"); } if (this.state != SetupStepState.NOT_FINISHED) { throw new DssException("This call is only allowed in NOT_FINISHED state!"); } if (this.mode == DssMode.HOST_SIDE) { /// Host side parsing if (this.beginArrived) { if (!DssRoot.IsInternalFormat(package.PackageFormat)) { this.packageListTmp.Add(package); } else { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_MSG_END && package.ReadInt(0) == this.stepID) { /// Finished this.packageList = this.packageListTmp.ToArray(); this.state = SetupStepState.READY; } else { /// Error this.state = SetupStepState.ERROR; } } } else /// end-if (this.beginArrived) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_AW_BEGIN && package.ReadInt(0) == this.stepID) { this.beginArrived = true; } else { /// Error this.state = SetupStepState.ERROR; } } } else /// end-if (this.mode == SetupStepMode.HOST_SIDE) { /// Guest side parsing if (this.beginArrived) { if (!DssRoot.IsInternalFormat(package.PackageFormat)) { this.packageListTmp.Add(package); } else { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_MSG_END && package.ReadInt(0) == this.stepID) { /// Finished this.packageList = this.packageListTmp.ToArray(); this.state = SetupStepState.READY; } else { /// Error this.state = SetupStepState.ERROR; } } } else /// end-if (this.beginArrived) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_RQ_BEGIN) { /// Read the step ID this.stepID = package.ReadInt(0); /// Read the left-list int[] leftList = package.ReadIntArray(1); bool err = false; for (int i = 0; i < leftList.Length; ++i) { if (leftList[i] < 0) { err = true; break; } } if (!err) { /// OK, save the list this.leftList = leftList; } else { /// Error this.state = SetupStepState.ERROR; return; } /// Read the lost-list int[] lostList = package.ReadIntArray(2); err = false; for (int i = 0; i < lostList.Length; ++i) { if (lostList[i] < 0) { err = true; break; } } if (!err) { /// OK, save the list this.lostList = lostList; } else { /// Error this.state = SetupStepState.ERROR; return; } /// Read the channel-state-list byte[] chStateBytes = package.ReadByteArray(3); if (chStateBytes.Length != 0) { this.channelStateList = new DssChannelState[chStateBytes.Length]; for (int i = 0; i < chStateBytes.Length; ++i) { if (chStateBytes[i] == (byte)DssChannelState.CHANNEL_CLOSED || chStateBytes[i] == (byte)DssChannelState.CHANNEL_OPENED || chStateBytes[i] == (byte)DssChannelState.GUEST_CONNECTED) { this.channelStateList[i] = (DssChannelState)chStateBytes[i]; } else { /// Error this.state = SetupStepState.ERROR; return; } } } /// end-if (chStateBytes != null && chStateBytes.Length != 0) else { /// Error this.state = SetupStepState.ERROR; return; } /// Everything is OK, the begin message arrived successfully. this.beginArrived = true; } else /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_RQ_BEGIN) { /// Error this.state = SetupStepState.ERROR; } } } }