/// <summary> /// Creates an instance of <see cref="PointerInputCmdMessage"/> with an optional Entity reference. /// </summary> /// <param name="tick">Client tick this was created.</param> /// <param name="inputFunctionId">Function this command is changing.</param> /// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param> /// <param name="uid">Entity that was under the pointer when the command was created.</param> public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, EntityCoordinates coordinates, EntityUid uid) : base(tick, subTick, inputFunctionId) { Coordinates = coordinates; Uid = uid; }
/// <summary> /// Creates an instance of <see cref="EventInputCmdMessage"/>. /// </summary> /// <param name="tick">Client tick this was created.</param> /// <param name="inputFunctionId">Function this command is changing.</param> public EventInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId) : base(tick, subTick, inputFunctionId) { }
/// <summary> /// Creates an instance of <see cref="PointerInputCmdMessage"/>. /// </summary> /// <param name="tick">Client tick this was created.</param> /// <param name="inputFunctionId">Function this command is changing.</param> /// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param> public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, EntityCoordinates coordinates) : this(tick, subTick, inputFunctionId, coordinates, EntityUid.Invalid) { }
public NeptuniumProductionEvent(Mine mine, GameTick occursAt) : base(occursAt, Priority.LowPriorty) { this._mine = mine; this._nextProduction = null; }
public WorldStateImmutable CreateDevWorldState() { var players = new List <PlayerImmutable>(); var gameTick = new GameTick(0); int playerCount = 5; for (int i = 0; i < playerCount; i++) { players.Add( new PlayerImmutable( PlayerId: PlayerIdFactory.Create($"discostu#{i}"), PlayerType: Id.PlayerType("terran"), Name: $"Commander Discostu#{i}", Created: DateTime.Now, State: new PlayerStateImmutable( LastGameTickUpdate: DateTime.Now, CurrentGameTick: gameTick, Resources: new Dictionary <ResourceDefId, decimal> { { Id.ResDef("land"), 50 }, { Id.ResDef("minerals"), 5000 }, { Id.ResDef("gas"), 3000 } }, Assets: new HashSet <AssetImmutable> { new AssetImmutable( AssetDefId: Id.AssetDef("commandcenter"), Level: 1 ), new AssetImmutable( AssetDefId: Id.AssetDef("factory"), Level: 1 ), new AssetImmutable( AssetDefId: Id.AssetDef("armory"), Level: 1 ), new AssetImmutable( AssetDefId: Id.AssetDef("spaceport"), Level: 1 ) }, Units: new List <UnitImmutable> { new UnitImmutable( UnitId: Id.NewUnitId(), UnitDefId: Id.UnitDef("wbf"), Count: 10, Position: null ), new UnitImmutable( UnitId: Id.NewUnitId(), UnitDefId: Id.UnitDef("spacemarine"), Count: 25, Position: null ), new UnitImmutable( UnitId: Id.NewUnitId(), UnitDefId: Id.UnitDef("siegetank"), Count: 3, Position: i == 0 ? null : PlayerIdFactory.Create("discostu#0") ), } ) ) ); } return(new WorldStateImmutable( players.ToDictionary(x => x.PlayerId), new GameTickStateImmutable(gameTick, DateTime.Now - TimeSpan.FromMinutes(1)), new List <GameActionImmutable>() )); }
/// <summary> /// Creates an instance of <see cref="StateInputCmdMessage"/>. /// </summary> /// <param name="tick">Client tick this was created.</param> /// <param name="inputFunctionId">Function this command is changing.</param> /// <param name="state">New state of the Input Function.</param> public StateInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, BoundKeyState state) : base(tick, subTick, inputFunctionId) { State = state; }
/// <summary> /// Advances the GameState by one tick. IMPORTANT: This does NOT apply any time machine actions. /// </summary> /// <returns>The next GameTick</returns> public GameTick GoToNextTick() { this.CurrentTick = this.CurrentTick.GetNextTick(); return(this.CurrentTick); }
protected NaturalGameEvent(GameTick occursAt, Priority priority) { this._eventId = Guid.NewGuid().ToString(); this._occursAt = occursAt; this._priority = priority; }
internal IList <GameAction> GetAndRemoveDueActions(PlayerId playerId, string name, GameTick gameTick) { // TODO: synchronize var actions = GetActions(playerId).Where(x => x.Name == name && x.IsDue(gameTick)).ToList(); // copy foreach (var action in actions) { Remove(action); } return(actions); }
/// <inheritdoc /> public void SendGameStateUpdate() { DebugTools.Assert(_networkManager.IsServer); _entityManager.Update(); if (!_networkManager.IsConnected) { // Prevent deletions piling up if we have no clients. _entityManager.CullDeletionHistory(GameTick.MaxValue); _mapManager.CullDeletionHistory(GameTick.MaxValue); return; } var inputSystem = _systemManager.GetEntitySystem <InputSystem>(); var oldestAck = GameTick.MaxValue; foreach (var channel in _networkManager.Channels) { var session = _playerManager.GetSessionByChannel(channel); if (session == null || session.Status != SessionStatus.InGame) { // client still joining, maybe iterate over sessions instead? continue; } if (!_ackedStates.TryGetValue(channel.ConnectionId, out var lastAck)) { DebugTools.Assert("Why does this channel not have an entry?"); } var entStates = lastAck == GameTick.Zero || !PvsEnabled ? _entityManager.GetEntityStates(lastAck) : _entityManager.UpdatePlayerSeenEntityStates(lastAck, session, _entityManager.MaxUpdateRange); var playerStates = _playerManager.GetPlayerStates(lastAck); var deletions = _entityManager.GetDeletedEntities(lastAck); var mapData = _mapManager.GetStateData(lastAck); // lastAck varies with each client based on lag and such, we can't just make 1 global state and send it to everyone var lastInputCommand = inputSystem.GetLastInputCommand(session); var lastSystemMessage = _entityNetworkManager.GetLastMessageSequence(session); var state = new GameState(lastAck, _gameTiming.CurTick, Math.Max(lastInputCommand, lastSystemMessage), entStates, playerStates, deletions, mapData); if (lastAck < oldestAck) { oldestAck = lastAck; } // actually send the state var stateUpdateMessage = _networkManager.CreateNetMessage <MsgState>(); stateUpdateMessage.State = state; _networkManager.ServerSendMessage(stateUpdateMessage, channel); // If the state is too big we let Lidgren send it reliably. // This is to avoid a situation where a state is so large that it consistently gets dropped // (or, well, part of it). // When we send them reliably, we immediately update the ack so that the next state will not be huge. if (stateUpdateMessage.ShouldSendReliably()) { _ackedStates[channel.ConnectionId] = _gameTiming.CurTick; } } // keep the deletion history buffers clean if (oldestAck > _lastOldestAck) { _lastOldestAck = oldestAck; _entityManager.CullDeletionHistory(oldestAck); _mapManager.CullDeletionHistory(oldestAck); } }
/// <summary> /// Constructor for the combat event /// </summary> /// <param name="combatant1">The first combatant</param> /// <param name="combatant2">The second combatant</param> /// <param name="tick">The tick the combat occurs</param> /// <param name="combatLocation">The location of the combat</param> public CombatEvent(ICombatable combatant1, ICombatable combatant2, GameTick tick) : base(tick, Priority.NATURAL_PRIORITY_9) { this._combatant1 = combatant1; this._combatant2 = combatant2; }
/// <summary> /// Creates any combat events that will result in the launch. /// </summary> private void CreateCombatEvents() { // Create the combat event for arrival RftVector targetPosition = this._destination.GetTargetPosition(_source.GetCurrentPosition(), this._launchedSub.GetSpeed()); GameTick arrival = this._launchTime.Advance((int)Math.Floor((targetPosition - _source.GetCurrentPosition()).Magnitude() / this._launchedSub.GetSpeed())); CombatEvent arriveCombat = new CombatEvent(this._launchedSub, this._destination, arrival, targetPosition); _combatEvents.Add(arriveCombat); Game.TimeMachine.AddEvent(arriveCombat); // Determine any combat events that may exist along the way. // First determine if any subs are on the same path. // Subs will only be on the same path if it is outpost to outpost if (this._destination.GetType().Equals(typeof(Outpost)) && this._source.GetType().Equals(typeof(Outpost))) { // Interpolate to launch time to determine combats! GameTick currentTick = Game.TimeMachine.GetCurrentTick(); Game.TimeMachine.GoTo(this.GetTick()); GameState interpolatedState = Game.TimeMachine.GetState(); foreach (Sub sub in interpolatedState.getSubsOnPath((Outpost)this._source, (Outpost)this._destination)) { // Don't combat with yourself if (sub == this.GetActiveSub()) { continue; } // Determine if we combat it if (sub.GetDirection() == this.GetActiveSub().GetDirection()) { if (this.GetActiveSub().GetExpectedArrival() < sub.GetExpectedArrival()) { // We can catch it. Determine when and create a combat event. } } else { // Sub is moving towards us. if (sub.GetOwner() != this.GetActiveSub().GetOwner()) { // Combat will occur // Determine when and create a combat event. // Determine the number of ticks between the incoming sub & the launched sub. int ticksBetweenSubs = sub.GetExpectedArrival() - this._launchTime; // Determine the speed ratio as a number between 0-0.5 double speedRatio = (sub.GetSpeed() / this.GetActiveSub().GetSpeed()) - 0.5; int ticksUntilCombat = (int)Math.Floor(speedRatio * ticksBetweenSubs); // Determine collision position: RftVector combatPosition = new RftVector(RftVector.Map, this.GetActiveSub().GetDirection() * ticksUntilCombat); CombatEvent combatEvent = new CombatEvent(sub, this.GetActiveSub(), this._launchTime.Advance(ticksUntilCombat), combatPosition); _combatEvents.Add(combatEvent); Game.TimeMachine.AddEvent(combatEvent); } } } // Go back to the original point in time. Game.TimeMachine.GoTo(currentTick); } }
/// <summary> /// Constructor for the combat event /// </summary> /// <param name="combatant1">The first combatant</param> /// <param name="combatant2">The second combatant</param> /// <param name="tick">The tick the combat occurs</param> public CombatEvent(Entity combatant1, Entity combatant2, GameTick tick) : base(tick, Priority.NaturalPriority9) { this._combatant1 = combatant1; this._combatant2 = combatant2; }
/// <inheritdoc /> public void SendGameStateUpdate() { DebugTools.Assert(_networkManager.IsServer); if (!_networkManager.IsConnected) { // Prevent deletions piling up if we have no clients. _entityManager.CullDeletionHistory(GameTick.MaxValue); _mapManager.CullDeletionHistory(GameTick.MaxValue); return; } var oldestAck = GameTick.MaxValue; var work = new List <(INetChannel channel, GameTick lastAck)>(); foreach (var channel in _networkManager.Channels) { var session = _playerManager.GetSessionByChannel(channel); if (session == null || session.Status != SessionStatus.InGame) { // client still joining, maybe iterate over sessions instead? continue; } if (!_ackedStates.TryGetValue(channel.ConnectionId, out var lastAck)) { DebugTools.Assert("Why does this channel not have an entry?"); } work.Add((channel, lastAck)); if (lastAck < oldestAck) { oldestAck = lastAck; } } var workDone = ParallelStates ? work.AsParallel().Select(GenerateStateFor).ToList() : work.Select(GenerateStateFor).ToList(); foreach (var(channel, state) in workDone) { // actually send the state var stateUpdateMessage = _networkManager.CreateNetMessage <MsgState>(); stateUpdateMessage.State = state; _networkManager.ServerSendMessage(stateUpdateMessage, channel); // If the state is too big we let Lidgren send it reliably. // This is to avoid a situation where a state is so large that it consistently gets dropped // (or, well, part of it). // When we send them reliably, we immediately update the ack so that the next state will not be huge. if (stateUpdateMessage.ShouldSendReliably()) { _ackedStates[channel.ConnectionId] = _gameTiming.CurTick; } } // keep the deletion history buffers clean if (oldestAck > _lastOldestAck) { _lastOldestAck = oldestAck; _entityManager.CullDeletionHistory(oldestAck); _mapManager.CullDeletionHistory(oldestAck); } }
/// <summary> /// Creates an instance of <see cref="FullInputCmdMessage"/>. /// </summary> /// <param name="tick">Client tick this was created.</param> /// <param name="inputSequence"></param> /// <param name="inputFunctionId">Function this command is changing.</param> /// <param name="state">New state of the Input Function.</param> /// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param> /// <param name="screenCoordinates"></param> public FullInputCmdMessage(GameTick tick, ushort subTick, int inputSequence, KeyFunctionId inputFunctionId, BoundKeyState state, EntityCoordinates coordinates, ScreenCoordinates screenCoordinates) : this(tick, subTick, inputFunctionId, state, coordinates, screenCoordinates, EntityUid.Invalid) { }
public static void Write(this NetOutgoingMessage message, GameTick tick) { message.Write(tick.Value); }
/// <summary> /// Creates an instance of <see cref="InputCmdMessage"/>. /// </summary> /// <param name="tick">Client tick this was created.</param> /// <param name="inputFunctionId">Function this command is changing.</param> public InputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId) { Tick = tick; SubTick = subTick; InputFunctionId = inputFunctionId; }
public void Dirty() { _lastStateUpdate = _timing.CurTick; }
/// <summary> /// Factory Constructor /// </summary> /// <param name="id">ID of the outpost</param> /// <param name="outpostStartPosition">Position of outpost</param> /// <param name="outpostOwner">Owner of outpost</param> public Factory(string id, RftVector outpostStartPosition, Player outpostOwner) : base(id, outpostStartPosition, outpostOwner) { this._ticksPerProduction = STANDARD_TICKS_PER_PRODUCTION; this._ticksToFirstProduction = new GameTick(STANDARD_TICKS_PER_PRODUCTION); //TODO: randomize this._drillersPerProduction = Constants.BaseFactoryProduction; }
public void Dirty(GameTick currentTick) { LastUpdate = currentTick; }
public GameStateMapData?GetStateData(GameTick fromTick) { var gridDatums = new Dictionary <GridId, GameStateMapData.GridDatum>(); foreach (var grid in _grids.Values) { if (grid.LastModifiedTick < fromTick) { continue; } var chunkData = new List <GameStateMapData.ChunkDatum>(); foreach (var(index, chunk) in grid.GetMapChunks()) { if (chunk.LastModifiedTick < fromTick) { continue; } var tileBuffer = new Tile[grid.ChunkSize * (uint)grid.ChunkSize]; // Flatten the tile array. // NetSerializer doesn't do multi-dimensional arrays. // This is probably really expensive. for (var x = 0; x < grid.ChunkSize; x++) { for (var y = 0; y < grid.ChunkSize; y++) { tileBuffer[x * grid.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y); } } chunkData.Add(new GameStateMapData.ChunkDatum(index, tileBuffer)); } var gridDatum = new GameStateMapData.GridDatum(chunkData.ToArray(), new MapCoordinates(grid.WorldPosition, grid.ParentMapId)); gridDatums.Add(grid.Index, gridDatum); } var mapDeletionsData = _mapDeletionHistory.Where(d => d.tick >= fromTick).Select(d => d.mapId).ToList(); var gridDeletionsData = _gridDeletionHistory.Where(d => d.tick >= fromTick).Select(d => d.gridId).ToList(); var mapCreations = _mapCreationTick.Where(kv => kv.Value >= fromTick && kv.Key != MapId.Nullspace) .ToDictionary(kv => kv.Key, kv => _defaultGrids[kv.Key]); var gridCreations = _grids.Values.Where(g => g.CreatedTick >= fromTick && g.ParentMapId != MapId.Nullspace).ToDictionary(g => g.Index, grid => new GameStateMapData.GridCreationDatum(grid.ChunkSize, grid.SnapSize, grid.IsDefaultGrid)); // no point sending empty collections if (gridDatums.Count == 0) { gridDatums = default; } if (gridDeletionsData.Count == 0) { gridDeletionsData = default; } if (mapDeletionsData.Count == 0) { mapDeletionsData = default; } if (mapCreations.Count == 0) { mapCreations = default; } if (gridCreations.Count == 0) { gridCreations = default; } // no point even creating an empty map state if no data if (gridDatums == null && gridDeletionsData == null && mapDeletionsData == null && mapCreations == null && gridCreations == null) { return(default);
public override void ReadFromBuffer(NetIncomingMessage buffer) { Sequence = new GameTick(buffer.ReadUInt32()); }