public static List <object> Serialize( GameEntity playerEntity, EntityState state, GameSnapshot snapshot, SerializationContext context) { var levelEntity = playerEntity.Position.LevelEntity; if (state == EntityState.Added) { var serializedLevel = LevelSnapshot.Serialize(levelEntity, null, null, context); return(PlayerSnapshot.Serialize(playerEntity, state, null, serializedLevel, context)); } else { if (!snapshot.SerializedLevels.TryGetValue(levelEntity.Id, out var serializedLevel)) { var position = playerEntity.Position; var level = context.DbContext.Entry(position).Property(p => p.LevelId); serializedLevel = LevelSnapshot.Serialize (position.LevelEntity, level.IsModified ? EntityState.Added : state, snapshot.LevelSnapshots.GetValueOrDefault(levelEntity.Id), context); snapshot.SerializedLevels[levelEntity.Id] = serializedLevel; } return(PlayerSnapshot.Serialize( playerEntity, state, snapshot.PlayerSnapshots[playerEntity.Id], serializedLevel, context)); } }
// TODO: Perf: Use change notifications instead public void Snapshot(GameDbContext dbContext, GameServices services) { SerializedLevels.Clear(); var snapshotedLevels = new List <int>(); var manager = dbContext.Manager; foreach (var playerEntity in manager.Players) { var context = new SerializationContext(dbContext, playerEntity, services); var levelEntity = playerEntity.Position.LevelEntity; LevelSnapshot levelSnapshot = null; if (!snapshotedLevels.Contains(levelEntity.Id)) { if (!LevelSnapshots.TryGetValue(levelEntity.Id, out levelSnapshot)) { levelSnapshot = new LevelSnapshot(); LevelSnapshots[levelEntity.Id] = levelSnapshot; } levelSnapshot.Snapshot(levelEntity, context); snapshotedLevels.Add(levelEntity.Id); } if (!PlayerSnapshots.TryGetValue(playerEntity.Id, out var playerSnapshot)) { playerSnapshot = new PlayerSnapshot(); PlayerSnapshots[playerEntity.Id] = playerSnapshot; } playerSnapshot.Snapshot(playerEntity, context); } if (snapshotedLevels.Count != LevelSnapshots.Count) { foreach (var levelId in LevelSnapshots.Keys.ToList()) { if (!snapshotedLevels.Contains(levelId)) { LevelSnapshots.Remove(levelId); } } } if (manager.Players.Count != PlayerSnapshots.Count) { foreach (var playerId in PlayerSnapshots.Keys.ToList()) { if (!manager.Players.ContainsEntity(playerId)) { PlayerSnapshots.Remove(playerId); } } } }
public List <object> QueryGame(string playerName, int intQueryType, int[] arguments) { var queryType = (GameQueryType)intQueryType; var result = new List <object> { intQueryType }; var player = FindPlayer(playerName); if (player?.Entity.Being.IsAlive != true) { return(result); } var manager = player.Entity.Manager; var context = new SerializationContext(_dbContext, player.Entity, _gameServices); switch (queryType) { case GameQueryType.SlottableAbilities: var slot = arguments[0]; result.Add(slot); var abilities = new List <object>(); result.Add(abilities); foreach (var slottableAbilityEntity in manager.AbilitiesToAffectableRelationship[player.EntityId]) { var ability = slottableAbilityEntity.Ability; if (!ability.IsUsable) { continue; } if (slot == AbilitySlottingSystem.DefaultMeleeAttackSlot) { if (ability.Template?.Type == AbilityType.DefaultAttack && ((WieldingAbility)ability.Template).ItemType == ItemType.WeaponMelee) { abilities.Add(AbilitySnapshot.Serialize(slottableAbilityEntity, null, context)); } } else if (slot == AbilitySlottingSystem.DefaultRangedAttackSlot) { if (ability.Template?.Type == AbilityType.DefaultAttack && ((WieldingAbility)ability.Template).ItemType == ItemType.WeaponRanged) { abilities.Add(AbilitySnapshot.Serialize(slottableAbilityEntity, null, context)); } } else if ((ability.Activation & ActivationType.Slottable) != 0 && ability.Template?.Type != AbilityType.DefaultAttack) { abilities.Add(AbilitySnapshot.Serialize(slottableAbilityEntity, null, context)); } } break; case GameQueryType.PlayerAttributes: result.Add(LevelActorSnapshot.SerializeAttributes(player.Entity, SenseType.Sight, context)); break; case GameQueryType.PlayerAdaptations: result.Add(PlayerSnapshot.SerializeAdaptations(player.Entity, context)); break; case GameQueryType.PlayerSkills: result.Add(PlayerSnapshot.SerializeSkills(player.Entity, context)); break; case GameQueryType.ActorAttributes: var actorKnowledge = manager.FindEntity(arguments[0])?.Knowledge; result.Add(LevelActorSnapshot.SerializeAttributes( actorKnowledge?.KnownEntity, actorKnowledge?.SensedType ?? SenseType.None, context)); break; case GameQueryType.ItemAttributes: var itemEntity = manager.FindEntity(arguments[0]); var item = itemEntity?.Item; var itemKnowledge = itemEntity?.Knowledge; if (item != null) { result.Add(InventoryItemSnapshot.SerializeAttributes(itemEntity, SenseType.Sight, context)); } else { result.Add(InventoryItemSnapshot.SerializeAttributes( itemKnowledge?.KnownEntity, itemKnowledge?.SensedType ?? SenseType.None, context)); } break; case GameQueryType.AbilityAttributes: var abilityEntity = manager.FindEntity(arguments[0]); var ownerEntity = abilityEntity.Ability.OwnerEntity; var activatorEntity = abilityEntity.Ability.OwnerEntity.HasComponent(EntityComponent.Item) ? player.Entity : ownerEntity; result.Add(AbilitySnapshot.SerializeAttributes(abilityEntity, activatorEntity, context)); break; default: throw new InvalidOperationException($"Query type {intQueryType} not supported"); } return(result); }
public static List <object> Serialize( GameEntity playerEntity, EntityState state, PlayerSnapshot snapshot, List <object> serializedLevel, SerializationContext context) { var manager = context.Manager; var player = playerEntity.Player; var being = playerEntity.Being; List <object> properties; if (state == EntityState.Added) { properties = new List <object>(14) { (int)state }; properties.Add(player.EntityId); properties.Add(player.ProperName); properties.Add(player.Game.CurrentTick); properties.Add(serializedLevel); properties.Add(manager.RacesToBeingRelationship[playerEntity.Id].Values .Select(r => RaceSnapshot.Serialize(r, null, context)).ToList()); properties.Add(manager.EntityItemsToContainerRelationship[playerEntity.Id] .Select(t => InventoryItemSnapshot.Serialize(t, null, null, context)).ToList()); properties.Add(GetSlottedAbilities(playerEntity, manager) .Select(a => AbilitySnapshot.Serialize(a, null, context)).ToList()); // TODO: Group log entries for the same tick // TODO: Only send entries since last player turn properties.Add(GetLogEntries(player) .Select(e => LogEntrySnapshot.Serialize(e, null, context)).ToList()); properties.Add(player.NextActionTick); properties.Add(player.NextLevelXP); properties.Add(being.ExperiencePoints); properties.Add(being.HitPoints); properties.Add(being.HitPointMaximum); properties.Add(being.EnergyPoints); properties.Add(being.EnergyPointMaximum); properties.Add(being.ReservedEnergyPoints); return(properties); } properties = new List <object>(3) { (int)state, snapshot.SnapshotTick, player.Game.CurrentTick }; var playerEntry = context.DbContext.Entry(player); var i = 2; if (serializedLevel != null) { properties.Add(i); properties.Add(serializedLevel); } i++; var serializedRaces = GameTransmissionProtocol.Serialize( manager.RacesToBeingRelationship[playerEntity.Id].Values, snapshot.RacesSnapshot, RaceSnapshot.Serialize, context); if (serializedRaces.Count > 0) { properties.Add(i); properties.Add(serializedRaces); } i++; var serializedItems = GameTransmissionProtocol.Serialize( manager.EntityItemsToContainerRelationship[playerEntity.Id], snapshot.ItemsSnapshot, InventoryItemSnapshot.Serialize, context); if (serializedItems.Count > 0) { properties.Add(i); properties.Add(serializedItems); } i++; var serializedAbilities = GameTransmissionProtocol.Serialize( GetSlottedAbilities(playerEntity, manager), snapshot.AbilitiesSnapshot, AbilitySnapshot.Serialize, context); if (serializedAbilities.Count > 0) { properties.Add(i); properties.Add(serializedAbilities); } i++; var serializedLog = GameTransmissionProtocol.Serialize( GetLogEntries(player), snapshot.LogEntriesSnapshot, LogEntrySnapshot.Serialize, context); if (serializedLog.Count > 0) { properties.Add(i); properties.Add(serializedLog); } i++; var nextActionTick = playerEntry.Property(nameof(PlayerComponent.NextActionTick)); if (nextActionTick.IsModified) { properties.Add(i); properties.Add(player.NextActionTick); } i++; var nextLevelXP = playerEntry.Property(nameof(PlayerComponent.NextLevelXP)); if (nextLevelXP.IsModified) { properties.Add(i); properties.Add(player.NextLevelXP); } var beingEntry = context.DbContext.Entry(being); if (beingEntry.State != EntityState.Unchanged) { i++; var xp = beingEntry.Property(nameof(BeingComponent.ExperiencePoints)); if (xp.IsModified) { properties.Add(i); properties.Add(being.ExperiencePoints); } i++; var hitPoints = beingEntry.Property(nameof(BeingComponent.HitPoints)); if (hitPoints.IsModified) { properties.Add(i); properties.Add(being.HitPoints); } i++; var hitPointMaximum = beingEntry.Property(nameof(BeingComponent.HitPointMaximum)); if (hitPointMaximum.IsModified) { properties.Add(i); properties.Add(being.HitPointMaximum); } i++; var energyPoints = beingEntry.Property(nameof(BeingComponent.EnergyPoints)); if (energyPoints.IsModified) { properties.Add(i); properties.Add(being.EnergyPoints); } i++; var energyPointMaximum = beingEntry.Property(nameof(BeingComponent.EnergyPointMaximum)); if (energyPointMaximum.IsModified) { properties.Add(i); properties.Add(being.EnergyPointMaximum); } i++; var reservedEnergyPoints = beingEntry.Property(nameof(BeingComponent.ReservedEnergyPoints)); if (reservedEnergyPoints.IsModified) { properties.Add(i); properties.Add(being.ReservedEnergyPoints); } } return(properties); }