/// <summary> /// Creates an RCCommand object from the given RCPackage. /// </summary> /// <param name="cmdPackage">The RCPackage to be deserialized.</param> /// <returns>The created RCCommand.</returns> public static RCCommand FromPackage(RCPackage cmdPackage) { if (cmdPackage == null) { throw new ArgumentNullException("cmdPackage"); } if (!cmdPackage.IsCommitted) { throw new ArgumentException("The incoming package is not committed!", "cmdPackage"); } if (cmdPackage.PackageFormat.ID != RCCommand.COMMAND_PACKAGEFORMAT) { throw new ArgumentException("The incoming package is not a command package!", "cmdPackage"); } RCNumVector targetPosition = RCNumVector.Undefined; int targetPositionX = cmdPackage.ReadInt(2); int targetPositionY = cmdPackage.ReadInt(3); if (targetPositionX != -1 && targetPositionY != -1) { targetPosition = new RCNumVector(new RCNumber(targetPositionX), new RCNumber(targetPositionY)); } return(new RCCommand( cmdPackage.ReadString(0), cmdPackage.ReadIntArray(1), targetPosition, cmdPackage.ReadInt(4), cmdPackage.ReadString(5))); }
/// <see cref="IScenarioLoader.LoadScenario"/> public Scenario LoadScenario(IMapAccess map, byte[] data) { if (map == null) { throw new ArgumentNullException("map"); } if (data == null) { throw new ArgumentNullException("data"); } /// Load the packages from the byte array. int offset = 0; Scenario scenario = new Scenario(map); while (offset < data.Length) { int parsedBytes; RCPackage package = RCPackage.Parse(data, offset, data.Length - offset, out parsedBytes); if (package == null || !package.IsCommitted) { throw new SimulatorException("Syntax error!"); } offset += parsedBytes; if (package.PackageFormat.ID == ScenarioFileFormat.MINERAL_FIELD) { IQuadTile quadTile = map.GetQuadTile(new RCIntVector(package.ReadShort(0), package.ReadShort(1))); MineralField mineralField = new MineralField(); mineralField.ResourceAmount.Write(package.ReadInt(2)); scenario.AddElementToScenario(mineralField); mineralField.AttachToMap(quadTile); } else if (package.PackageFormat.ID == ScenarioFileFormat.VESPENE_GEYSER) { IQuadTile quadTile = map.GetQuadTile(new RCIntVector(package.ReadShort(0), package.ReadShort(1))); VespeneGeyser vespeneGeyser = new VespeneGeyser(); vespeneGeyser.ResourceAmount.Write(package.ReadInt(2)); scenario.AddElementToScenario(vespeneGeyser); vespeneGeyser.AttachToMap(quadTile); } else if (package.PackageFormat.ID == ScenarioFileFormat.START_LOCATION) { IQuadTile quadTile = map.GetQuadTile(new RCIntVector(package.ReadShort(0), package.ReadShort(1))); StartLocation startLocation = new StartLocation(package.ReadByte(2)); scenario.AddElementToScenario(startLocation); startLocation.AttachToMap(quadTile); } } /// Check the constraints of the visible entities. foreach (Entity entity in scenario.GetElementsOnMap <Entity>(MapObjectLayerEnum.GroundObjects, MapObjectLayerEnum.AirObjects)) { if (entity.CheckPlacementConstraints(entity.MapObject.QuadraticPosition.Location, new RCSet <Entity>()).Count != 0) { throw new MapException(string.Format("Entity at {0} is voilating its placement constraints!", entity.MapObject.QuadraticPosition.Location)); } } return(scenario); }
/// <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> /// Creates a MapHeader structure from the given RCPackage. /// </summary> /// <param name="package">The RCPackage that contains the map header informations.</param> /// <returns>The created MapHeader structure.</returns> public static MapHeader FromPackage(RCPackage package) { if (package == null) { throw new ArgumentNullException("package"); } if (!package.IsCommitted) { throw new ArgumentException("The header package is not committed!", "package"); } if (package.PackageType != RCPackageType.CUSTOM_DATA_PACKAGE) { throw new ArgumentException("Invalid package type!", "package"); } if (package.PackageFormat.ID != MapFileFormat.MAP_HEADER) { throw new ArgumentException("Invalid package format!", "package"); } MapHeader header = new MapHeader(); header.appVersion = new Version(package.ReadInt(0), package.ReadInt(1), package.ReadInt(2), package.ReadInt(3)); header.mapName = package.ReadString(4); header.tilesetName = package.ReadString(5); header.mapSize = new RCIntVector(package.ReadShort(6), package.ReadShort(7)); header.maxPlayers = package.ReadByte(8); header.checksumList = new List <int>(package.ReadIntArray(9)); if (header.mapName == null) { throw new MapException("Map name information is missing!"); } if (header.tilesetName == null) { throw new MapException("Tileset name information is missing!"); } if (header.mapSize.X <= 0 || header.mapSize.Y <= 0) { throw new MapException("Map size cannot be negative or 0!"); } if (header.maxPlayers <= 0) { throw new MapException("Maximum number of players cannot be negative or 0!"); } return(header); }
/// <summary> /// Registers the given commit answer arrived from the given operator. /// </summary> /// <param name="commit">The arrived commit answer package.</param> /// <param name="senderID">The sender operator.</param> /// <returns>True in case of success, false otherwise.</returns> public bool RegisterCommitAnswer(RCPackage commitAw, 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 (commitAw == null || !commitAw.IsCommitted) { throw new ArgumentException("commitAw"); } if (senderID == this.root.IdOfThisPeer) { throw new DssException("Unexpected commit answer sender!"); } if (senderID < 0 || senderID >= this.root.OpCount) { throw new ArgumentException("senderID"); } if (commitAw.PackageFormat.ID != DssRoot.DSS_COMMIT_ANSWER) { /// Package format error. return(false); } int ticket = commitAw.ReadInt(0); if (!this.commitAwMonitors.ContainsKey(ticket)) { /// Unexpected ticket. return(false); } CommitMonitor monitor = this.commitAwMonitors[ticket]; int pingTime = -1; if (monitor.AnswerArrived(senderID, out pingTime)) { this.aptCalculators[senderID].NewItem(pingTime); if (monitor.IsCommitAnswered) { UnregisterCommitMonitor(ticket); } return(true); } else { /// Ping already answered by the sender. return(false); } }
/// <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 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> /// Called when a control packages arrived from the host during setup stage. /// </summary> /// <param name="package">The arrived control package.</param> public void SetupStageCtrlPackage(RCPackage package) { if (this.sm.CurrentState == this.WaitingConnectionACK) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_ACK) { 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)) { this.WaitingConnectionACK_WaitingSetupStepRQ.Fire(); this.controller.ExecuteFirings(); return; } else { TraceManager.WriteAllTrace(string.Format("Incompatible with host version: {0} (RC.DssServices)", otherVer), DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } } else { TraceManager.WriteAllTrace("Unable to parse version information!", DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_ACK) else if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_REJECT) { TraceManager.WriteAllTrace(string.Format("Connection request rejected by the host! Reason: {0}", package.ReadString(0)), DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } else { TraceManager.WriteAllTrace("Unexpected answer from host to connection request!", DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } } else if (this.sm.CurrentState == this.WaitingSetupStepRQ) { if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) { /// The host left the DSS this.guestRoot.SetupIface.HostLeftDss(); StopTimeouts(); this.guestRoot.EventQueue.ExitEventLoop(); return; } /// end-if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) else if (package.PackageFormat.ID == DssRoot.DSS_CTRL_DROP_GUEST) { /// The current guest has been dropped out of the DSS by the host. this.guestRoot.SetupIface.DroppedByHost(); StopTimeouts(); this.guestRoot.EventQueue.ExitEventLoop(); return; } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_DROP_GUEST) else if (package.PackageFormat.ID == DssRoot.DSS_CTRL_START_SIMULATION) { int[] leftList = null; int[] lostList = null; bool[] opFlags = null; if (ParseStartSimPackage(package, out leftList, out lostList, out opFlags)) { StopTimeouts(); CallNotificationMethods(leftList, lostList); this.guestRoot.SetupIface.SimulationStarted(); this.opFlagsTmp = opFlags; this.WaitingSetupStepRQ_Simulating.Fire(); this.controller.ExecuteFirings(); return; } else { SetupStageError(); return; } } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_START_SIMULATION) else { this.guestRoot.Step.IncomingPackage(package); if (this.guestRoot.Step.State == SetupStepState.READY) { /// Setup step request arrived. this.WaitingSetupStepRQ_SendingSetupStepAW.Fire(); this.controller.ExecuteFirings(); return; } else if (this.guestRoot.Step.State == SetupStepState.ERROR) { /// Setup step request error. SetupStageError(); return; } else { /// Setup step request not finished yet, more packages to wait. return; } } } /// end-if (this.sm.CurrentState == this.WaitingSetupStepRQ) else { /// Go to error state if the package cannot be handled until now. SetupStageError(); return; } }
/// <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> /// 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; } } } }