From 6630850b772574df21bf34c219905b681f7f9e9f Mon Sep 17 00:00:00 2001 From: Killfrra Date: Sun, 1 May 2022 08:42:20 +0300 Subject: [PATCH] Progress #1407 -- Moving pieces of code to where they should be (#1439) For `GameObject`s, the `LateUpdate`, `Sync`, `OnAfterSync` and `OnReconnect` functions have been introduced. The next step is to move the some of the function calls from `PacketNotifier` to `OnEnter/LeaveVision`, `OnSync` and `OnSpawn`. --- .../Domain/GameObjects/IGameObject.cs | 30 +++- .../AttackableUnits/AI/BaseTurret.cs | 2 + .../AttackableUnits/AI/LaneMinion.cs | 2 + .../AttackableUnits/AI/LaneTurret.cs | 12 ++ .../AttackableUnits/AI/ObjAIBase.cs | 31 ++++- .../AttackableUnits/AttackableUnit.cs | 21 +++ .../AttackableUnits/Buildings/ObjBuilding.cs | 2 + GameServerLib/GameObjects/GameObject.cs | 73 +++++++++- GameServerLib/GameObjects/Particle.cs | 3 + .../GameObjects/Spell/Missile/SpellMissile.cs | 3 + GameServerLib/ObjectManager.cs | 130 ++++-------------- .../Packets/PacketHandlers/HandleSpawn.cs | 24 +--- 12 files changed, 212 insertions(+), 121 deletions(-) diff --git a/GameServerCore/Domain/GameObjects/IGameObject.cs b/GameServerCore/Domain/GameObjects/IGameObject.cs index f99ddc049..2ec4fafda 100644 --- a/GameServerCore/Domain/GameObjects/IGameObject.cs +++ b/GameServerCore/Domain/GameObjects/IGameObject.cs @@ -44,6 +44,16 @@ public interface IGameObject : IUpdate /// Radius of the circle which is used for vision; detecting if objects are visible given terrain, and if so, networked to the player (or team) that owns this game object. /// float VisionRadius { get; } + /// + /// Whether the object should be hidden by the fog of war. + /// + bool IsAffectedByFoW { get; } + /// + /// If an object is to be hidden by the fog of war, + /// should the object's spawn notification be sent only when the object comes into view, + /// not as soon as possible. + /// + bool SpawnShouldBeHidden { get; } /// /// Called by ObjectManager after AddObject (usually right after instatiation of GameObject). @@ -104,6 +114,24 @@ public interface IGameObject : IUpdate /// void OnCollision(IGameObject collider, bool isTerrain = false); + /// + /// Called by ObjectManager after the Update function has been called on all objects. + /// Designed to be used by AI to determine the target. + /// + void LateUpdate(float diff); + /// + /// Called by the ObjectManager after vision has been computed for this object and a particular player. + /// + void Sync(int userId, TeamId team, bool visible, bool forceSpawn = false); + /// + /// Called by ObjectManager after the Sync function has been called on all objects. + /// + void OnAfterSync(); + /// + /// Called by HandleSpawn class after the player has reconnected. + /// + void OnReconnect(int userId, TeamId team); + /// /// Sets the object's team. /// @@ -128,7 +156,7 @@ public interface IGameObject : IUpdate /// A team which could have vision of this object. /// New value. void SetVisibleByTeam(TeamId team, bool visible = true); - + /// /// Whether or not the object is visible for the specified player. /// diff --git a/GameServerLib/GameObjects/AttackableUnits/AI/BaseTurret.cs b/GameServerLib/GameObjects/AttackableUnits/AI/BaseTurret.cs index d16f336fe..bc0a86d78 100644 --- a/GameServerLib/GameObjects/AttackableUnits/AI/BaseTurret.cs +++ b/GameServerLib/GameObjects/AttackableUnits/AI/BaseTurret.cs @@ -39,6 +39,8 @@ public class BaseTurret : ObjAiBase, IBaseTurret /// public IRegion BubbleRegion { get; private set; } + public override bool IsAffectedByFoW => false; + public BaseTurret( Game game, string name, diff --git a/GameServerLib/GameObjects/AttackableUnits/AI/LaneMinion.cs b/GameServerLib/GameObjects/AttackableUnits/AI/LaneMinion.cs index 79d1ca84a..d5207fb86 100644 --- a/GameServerLib/GameObjects/AttackableUnits/AI/LaneMinion.cs +++ b/GameServerLib/GameObjects/AttackableUnits/AI/LaneMinion.cs @@ -19,6 +19,8 @@ public class LaneMinion : Minion, ILaneMinion public string BarracksName { get; } public MinionSpawnType MinionSpawnType { get; } + public override bool SpawnShouldBeHidden => false; + public LaneMinion( Game game, MinionSpawnType spawnType, diff --git a/GameServerLib/GameObjects/AttackableUnits/AI/LaneTurret.cs b/GameServerLib/GameObjects/AttackableUnits/AI/LaneTurret.cs index 673cae8bd..aeb8418c3 100644 --- a/GameServerLib/GameObjects/AttackableUnits/AI/LaneTurret.cs +++ b/GameServerLib/GameObjects/AttackableUnits/AI/LaneTurret.cs @@ -97,5 +97,17 @@ public override void AutoAttackHit(IAttackableUnit target) base.AutoAttackHit(target); } } + + protected override void OnSpawn(int userId, TeamId team, bool doVision) + { + base.OnSpawn(userId, team, doVision); + foreach (var item in Inventory) + { + if (item != null) + { + _game.PacketNotifier.NotifyBuyItem(userId, this, item as IItem); + } + } + } } } diff --git a/GameServerLib/GameObjects/AttackableUnits/AI/ObjAIBase.cs b/GameServerLib/GameObjects/AttackableUnits/AI/ObjAIBase.cs index 2067acb88..e7a007183 100644 --- a/GameServerLib/GameObjects/AttackableUnits/AI/ObjAIBase.cs +++ b/GameServerLib/GameObjects/AttackableUnits/AI/ObjAIBase.cs @@ -1064,6 +1064,9 @@ public void SetPet(IPet pet) public override void Update(float diff) { base.Update(diff); + + UpdateBuffs(diff); + CharScript.OnUpdate(diff); if (!_aiPaused) { @@ -1093,7 +1096,33 @@ public override void Update(float diff) SetPet(null); } } - + void UpdateBuffs(float diff) + { + var tempBuffs = new List(GetBuffs()); + for (int i = tempBuffs.Count - 1; i >= 0; i--) + { + if (tempBuffs[i].Elapsed()) + { + RemoveBuff(tempBuffs[i]); + } + else + { + tempBuffs[i].Update(diff); + } + } + } + public override void LateUpdate(float diff) + { + // Stop targeting an untargetable unit. + if (TargetUnit != null && !TargetUnit.Status.HasFlag(StatusFlags.Targetable)) + { + if(TargetUnit is IObjAiBase aiTar && aiTar.CharData.IsUseable) + { + return; + } + Untarget(TargetUnit); + } + } public override void TakeDamage(IAttackableUnit attacker, float damage, DamageType type, DamageSource source, DamageResultType damageText) { base.TakeDamage(attacker, damage, type, source, damageText); diff --git a/GameServerLib/GameObjects/AttackableUnits/AttackableUnit.cs b/GameServerLib/GameObjects/AttackableUnits/AttackableUnit.cs index 813cc2f19..d5969b951 100644 --- a/GameServerLib/GameObjects/AttackableUnits/AttackableUnit.cs +++ b/GameServerLib/GameObjects/AttackableUnits/AttackableUnit.cs @@ -92,6 +92,9 @@ public class AttackableUnit : GameObject, IAttackableUnit /// public IForceMovementParameters MovementParameters { get; protected set; } + public override bool IsAffectedByFoW => true; + public override bool SpawnShouldBeHidden => true; + public AttackableUnit( Game game, string model, @@ -273,6 +276,24 @@ public override void OnCollision(IGameObject collider, bool isTerrain = false) } } + protected override void OnSync(int userId, TeamId team) + { + if (Replication.Changed) + { + _game.PacketNotifier.HoldReplicationDataUntilOnReplicationNotification(this, userId, true); + } + if (IsMovementUpdated()) + { + _game.PacketNotifier.HoldMovementDataUntilWaypointGroupNotification(this, userId, false); + } + } + + public override void OnAfterSync() + { + Replication.MarkAsUnchanged(); + ClearMovementUpdated(); + } + /// /// Returns whether or not this unit is targetable to the specified team. /// diff --git a/GameServerLib/GameObjects/AttackableUnits/Buildings/ObjBuilding.cs b/GameServerLib/GameObjects/AttackableUnits/Buildings/ObjBuilding.cs index 877d670de..467b83928 100644 --- a/GameServerLib/GameObjects/AttackableUnits/Buildings/ObjBuilding.cs +++ b/GameServerLib/GameObjects/AttackableUnits/Buildings/ObjBuilding.cs @@ -6,6 +6,8 @@ namespace LeagueSandbox.GameServer.GameObjects.AttackableUnits.Buildings { public class ObjBuilding : AttackableUnit, IObjBuilding { + public override bool IsAffectedByFoW => false; + public ObjBuilding(Game game, string model, IStats stats, int collisionRadius = 40, Vector2 position = new Vector2(), int visionRadius = 0, uint netId = 0, TeamId team = TeamId.TEAM_BLUE) : base(game, model, stats, collisionRadius, position, visionRadius, netId, team) diff --git a/GameServerLib/GameObjects/GameObject.cs b/GameServerLib/GameObjects/GameObject.cs index 12a9ec4ce..8a96c14d4 100644 --- a/GameServerLib/GameObjects/GameObject.cs +++ b/GameServerLib/GameObjects/GameObject.cs @@ -81,6 +81,9 @@ public IEnumerable VisibleForPlayers /// public float VisionRadius { get; protected set; } + public virtual bool IsAffectedByFoW => false; + public virtual bool SpawnShouldBeHidden => false; + /// /// Instantiation of an object which represents the base class for all objects in League of Legends. /// @@ -132,6 +135,10 @@ public virtual void Update(float diff) { } + public virtual void LateUpdate(float diff) + { + } + /// /// Whether or not the object should be removed from the game (usually both server and client-side). Refer to ObjectManager. /// @@ -242,6 +249,68 @@ public virtual void OnCollision(IGameObject collider, bool isTerrain = false) } } + protected virtual void OnSpawn(int userId, TeamId team, bool doVision) + { + _game.PacketNotifier.NotifySpawn(this, team, userId, _game.GameTime, doVision); + } + + protected virtual void OnEnterVision(int userId, TeamId team) + { + _game.PacketNotifier.NotifyVisibilityChange(this, team, true, userId); + } + + protected virtual void OnSync(int userId, TeamId team) + { + } + + protected virtual void OnLeaveVision(int userId, TeamId team) + { + _game.PacketNotifier.NotifyVisibilityChange(this, team, false, userId); + } + + public virtual void Sync(int userId, TeamId team, bool visible, bool forceSpawn = false) + { + visible = visible || !IsAffectedByFoW; + + if (!forceSpawn && IsSpawnedForPlayer(userId)) + { + if (IsAffectedByFoW && (IsVisibleForPlayer(userId) != visible)) + { + if(visible) + { + OnEnterVision(userId, team); + } + else + { + OnLeaveVision(userId, team); + } + SetVisibleForPlayer(userId, visible); + } + else if(visible) + { + OnSync(userId, team); + } + } + else if (visible || !SpawnShouldBeHidden) + { + OnSpawn(userId, team, visible); + SetVisibleForPlayer(userId, visible); + SetSpawnedForPlayer(userId); + } + } + + public virtual void OnAfterSync() + { + } + + public virtual void OnReconnect(int userId, TeamId team) + { + if(IsSpawnedForPlayer(userId)) + { + OnSpawn(userId, team, IsVisibleForPlayer(userId)); + } + } + /// /// Sets the object's team. /// @@ -263,7 +332,7 @@ public virtual void SetTeam(TeamId team) /// A team which could have vision of this object. public bool IsVisibleByTeam(TeamId team) { - return _visibleByTeam[team]; + return !IsAffectedByFoW || _visibleByTeam[team]; } /// @@ -283,7 +352,7 @@ public void SetVisibleByTeam(TeamId team, bool visible = true) /// The player in relation to which the value is obtained public bool IsVisibleForPlayer(int userId) { - return _visibleForPlayers.GetValueOrDefault(userId, false); + return !IsAffectedByFoW || _visibleForPlayers.GetValueOrDefault(userId, false); } /// diff --git a/GameServerLib/GameObjects/Particle.cs b/GameServerLib/GameObjects/Particle.cs index d6b8adfb3..a2790447b 100644 --- a/GameServerLib/GameObjects/Particle.cs +++ b/GameServerLib/GameObjects/Particle.cs @@ -73,6 +73,9 @@ public class Particle : GameObject, IParticle /// public FXFlags Flags { get; } + public override bool IsAffectedByFoW => true; + public override bool SpawnShouldBeHidden => true; + /// /// Prepares the Particle, setting up the information required for networking it to clients. /// This particle will spawn and stay on the specified GameObject target. diff --git a/GameServerLib/GameObjects/Spell/Missile/SpellMissile.cs b/GameServerLib/GameObjects/Spell/Missile/SpellMissile.cs index 9d217c030..0cd4a6156 100644 --- a/GameServerLib/GameObjects/Spell/Missile/SpellMissile.cs +++ b/GameServerLib/GameObjects/Spell/Missile/SpellMissile.cs @@ -36,6 +36,9 @@ public class SpellMissile : GameObject, ISpellMissile /// public bool IsServerOnly { get; } + public override bool IsAffectedByFoW => true; + public override bool SpawnShouldBeHidden => true; + public SpellMissile( Game game, int collisionRadius, diff --git a/GameServerLib/ObjectManager.cs b/GameServerLib/ObjectManager.cs index 9a3b9000e..2a9d1a1d7 100644 --- a/GameServerLib/ObjectManager.cs +++ b/GameServerLib/ObjectManager.cs @@ -68,11 +68,6 @@ public ObjectManager(Game game) } } - bool IsAffectedByVision(IGameObject obj) - { - return obj is IParticle || obj is IAttackableUnit || obj is ISpellMissile; - } - /// /// Function called every tick of the game. /// @@ -105,7 +100,7 @@ public void Update(float diff) foreach (var obj in _objects.Values) { - LateUpdate(obj, diff); + obj.LateUpdate(diff); } foreach (var obj in _objectsToAdd) @@ -125,11 +120,7 @@ public void Update(float diff) UpdateVisionSpawnAndSync(obj, kv.Item2); } - if (obj is IAttackableUnit u) - { - u.Replication.MarkAsUnchanged(); - u.ClearMovementUpdated(); - } + obj.OnAfterSync(); } _game.PacketNotifier.NotifyOnReplication(); @@ -151,6 +142,24 @@ public void SpawnObject(IGameObject obj) { UpdateVisionSpawnAndSync(obj, kv.Item2, forceSpawn: true); } + + obj.OnAfterSync(); + } + + public void OnReconnect(int userId, TeamId team) + { + foreach (IGameObject obj in _objects.Values) + { + obj.OnReconnect(userId, team); + } + } + + public void SpawnObjects(ClientInfo clientInfo) + { + foreach (IGameObject obj in _objects.Values) + { + UpdateVisionSpawnAndSync(obj, clientInfo, forceSpawn: true); + } } /// @@ -158,13 +167,10 @@ public void SpawnObject(IGameObject obj) /// void UpdateTeamsVision(IGameObject obj) { - if (IsAffectedByVision(obj)) - { foreach (var team in Teams) { - obj.SetVisibleByTeam(team, TeamHasVisionOn(team, obj)); + obj.SetVisibleByTeam(team, !obj.IsAffectedByFoW || TeamHasVisionOn(team, obj)); } - } } /// @@ -177,93 +183,13 @@ public void UpdateVisionSpawnAndSync(IGameObject obj, ClientInfo clientInfo, boo IChampion champion = clientInfo.Champion; bool nearSighted = champion.Status.HasFlag(StatusFlags.NearSighted); - bool isAffectedByVision = IsAffectedByVision(obj); - bool shouldBeVisibleForPlayer = !isAffectedByVision || ( + bool shouldBeVisibleForPlayer = !obj.IsAffectedByFoW || ( nearSighted ? UnitHasVisionOn(champion, obj) : obj.IsVisibleByTeam(champion.Team) ); - if (!forceSpawn && obj.IsSpawnedForPlayer(pid)) - { - if (isAffectedByVision && (obj.IsVisibleForPlayer(pid) != shouldBeVisibleForPlayer)) - { - _game.PacketNotifier.NotifyVisibilityChange(obj, team, shouldBeVisibleForPlayer, pid); - obj.SetVisibleForPlayer(pid, shouldBeVisibleForPlayer); - } - else if(shouldBeVisibleForPlayer) - { - Sync(obj, pid); - } - } - else if (shouldBeVisibleForPlayer || !( - //bool spawnShouldBeHidden = - obj is IParticle || obj is ISpellMissile || (obj is IMinion && !(obj is ILaneMinion)) - )) - { - _game.PacketNotifier.NotifySpawn(obj, team, pid, _game.GameTime, shouldBeVisibleForPlayer); - obj.SetVisibleForPlayer(pid, shouldBeVisibleForPlayer); - obj.SetSpawnedForPlayer(pid); - - if (obj is ILaneTurret turret) - { - foreach (var item in turret.Inventory) - { - if (item != null) - { - _game.PacketNotifier.NotifyBuyItem(pid, turret, item as IItem); - } - } - } - } - } - - void Sync(IGameObject obj, int userId = 0) - { - if (obj is IAttackableUnit u) - { - if(u.Replication.Changed) - { - _game.PacketNotifier.HoldReplicationDataUntilOnReplicationNotification(u, userId, true); - } - - if (u.IsMovementUpdated()) - { - _game.PacketNotifier.HoldMovementDataUntilWaypointGroupNotification(u, userId, false); - } - } - } - - void LateUpdate(IGameObject obj, float diff) - { - if (obj is IAttackableUnit u) - { - if (u is IObjAiBase ai) - { - var tempBuffs = new List(ai.GetBuffs()); - for (int i = tempBuffs.Count - 1; i >= 0; i--) - { - if (tempBuffs[i].Elapsed()) - { - ai.RemoveBuff(tempBuffs[i]); - } - else - { - tempBuffs[i].Update(diff); - } - } - - // Stop targeting an untargetable unit. - if (ai.TargetUnit != null && !ai.TargetUnit.Status.HasFlag(StatusFlags.Targetable)) - { - if(ai.TargetUnit is IObjAiBase aiTar && aiTar.CharData.IsUseable) - { - return; - } - StopTargeting(ai.TargetUnit); - } - } - } + obj.Sync(pid, team, shouldBeVisibleForPlayer, forceSpawn); } /// @@ -359,6 +285,11 @@ public bool TeamHasVisionOn(TeamId team, IGameObject o) { if (o != null) { + if(!o.IsAffectedByFoW) + { + return true; + } + foreach (var p in _visionProviders[team]) { if (UnitHasVisionOn(p, o)) @@ -367,15 +298,14 @@ public bool TeamHasVisionOn(TeamId team, IGameObject o) } } } - return false; } bool UnitHasVisionOn(IGameObject observer, IGameObject tested) { - if(tested is IBaseTurret || tested is IObjBuilding) + if(!tested.IsAffectedByFoW) { - //return true; + return true; } if(tested is IParticle particle) diff --git a/GameServerLib/Packets/PacketHandlers/HandleSpawn.cs b/GameServerLib/Packets/PacketHandlers/HandleSpawn.cs index 055218e13..1370af601 100644 --- a/GameServerLib/Packets/PacketHandlers/HandleSpawn.cs +++ b/GameServerLib/Packets/PacketHandlers/HandleSpawn.cs @@ -108,24 +108,14 @@ public override bool HandlePacket(int userId, SpawnRequest req) } } - var objects = _game.ObjectManager.GetObjects(); - foreach (var obj in objects.Values) + var om = _game.ObjectManager as ObjectManager; + if (_game.IsRunning) { - if (!(obj is IChampion)) - { - if (_game.IsRunning) - { - if (obj.IsSpawnedForPlayer(userId)) - { - bool isVisibleForPlayer = obj.IsVisibleForPlayer(userId); - _game.PacketNotifier.NotifySpawn(obj, userInfo.Team, userId, _game.GameTime, isVisibleForPlayer); - } - } - else - { - (_game.ObjectManager as ObjectManager).UpdateVisionSpawnAndSync(obj, userInfo, forceSpawn: true); - } - } + om.OnReconnect(userId, userInfo.Team); + } + else + { + om.SpawnObjects(userInfo); } _game.PacketNotifier.NotifySpawnEnd(userId);