From 1eb6b3e9e62d46f71be04f0afc17c3deaf058e3b Mon Sep 17 00:00:00 2001 From: laurids Date: Mon, 17 Jul 2023 23:08:58 +0200 Subject: [PATCH] (WIP) Unity Netcode Part 2 --- Assets/GWConquest/Scripts/District.cs | 7 +- Assets/GWConquest/Scripts/DistrictFactory.cs | 7 ++ Assets/GWConquest/Scripts/Formation.cs | 56 +++++----- Assets/GWConquest/Scripts/GWNetworkArray.cs | 2 +- Assets/GWConquest/Scripts/Inventory.cs | 24 ++++ Assets/GWConquest/Scripts/Player.cs | 6 + Assets/GWConquest/Scripts/ServerCallbacks.cs | 104 ++++------------- .../GWConquest/Scripts/UI/BattleUnitIcon.cs | 8 +- Assets/GWConquest/Scripts/Unit.cs | 105 +++++++++++------- Assets/GWConquest/Scripts/Util.cs | 11 ++ 10 files changed, 175 insertions(+), 155 deletions(-) diff --git a/Assets/GWConquest/Scripts/District.cs b/Assets/GWConquest/Scripts/District.cs index d141d45..b18f9df 100644 --- a/Assets/GWConquest/Scripts/District.cs +++ b/Assets/GWConquest/Scripts/District.cs @@ -8,7 +8,7 @@ using Unity.Collections; namespace GWConquest { - public class District : NetworkBehaviour + public class District : NetworkBehaviour, IHasInventory { public static List AllDistricts = new List(); public static List AllConnections = new List(); @@ -333,6 +333,11 @@ namespace GWConquest upgrades.Add(upgradeID); } + public IInventory GetInventory() + { + return Inventory; + } + } public enum DistrictType diff --git a/Assets/GWConquest/Scripts/DistrictFactory.cs b/Assets/GWConquest/Scripts/DistrictFactory.cs index f64d59d..e1630bf 100644 --- a/Assets/GWConquest/Scripts/DistrictFactory.cs +++ b/Assets/GWConquest/Scripts/DistrictFactory.cs @@ -163,6 +163,13 @@ namespace GWConquest } + [ServerRpc] + public void AddProductionQueueEntryRpc(ushort buildableID, bool isUpgrade, NetworkBehaviourReference player) + { + IBuildable buildable = isUpgrade ? DistrictUpgrade.FromID(buildableID) : UnitClass.FromID(buildableID); + AddProductionQueueEntry(buildable, player.GetBehaviour()); + } + public float GetBuildTimeForBuildable(IBuildable buildable) { return buildable.BuildTime * GameManager.Instance.TimeScale; diff --git a/Assets/GWConquest/Scripts/Formation.cs b/Assets/GWConquest/Scripts/Formation.cs index 2dded86..d0ee3d5 100644 --- a/Assets/GWConquest/Scripts/Formation.cs +++ b/Assets/GWConquest/Scripts/Formation.cs @@ -25,7 +25,7 @@ namespace GWConquest } - public class Formation : NetworkBehaviour, IMovable + public class Formation : NetworkBehaviour, IMovable, IHasInventory { public static List AllFormations = new List(); @@ -284,39 +284,29 @@ namespace GWConquest BoltLog.Info($"Formation {this} has not enough fuel for transition {path[0]} -> {path[1]}"); return; } - MoveFormationEvent evnt = MoveFormationEvent.Create(GlobalTargets.OnlyServer); - evnt.Formation = entity; - //evnt.TargetZone = Zone.GetZoneId(path[1]); - if(path.Count > 1) - { - var zoneListToken = new ZoneListToken().SetEntries( - path.GetRange(1, path.Count-1).ToArray()); - evnt.PathQueue = zoneListToken; - } - if(targetFormation != null) - { - evnt.TargetFormation = targetFormation.entity; - } - evnt.Send(); + int[] zoneIDs = path.GetRange(1, path.Count-1).Select(z => Zone.GetZoneId(z)).ToArray(); + BeginMovementServerRpc(zoneIDs, targetFormation); } else { BoltLog.Info("Could not move formation {0} (controlled: {1}, movement state: {2})", - this, entity.IsControlled, MovementState); + this, IsOwner, MovementState); } } - public void BeginMovementServer(Zone[] path, Formation targetFormation=null) + public void BeginMovementServer(int[] zoneIDs, Formation targetFormation) { - if(path == null) + + if(zoneIDs == null) { throw new ArgumentNullException("Movement path is null!"); } - if(!CanMoveTo(currentZone, path[0])) + Zone firstZone = Zone.GetFromId(zoneIDs[0]); + if(!CanMoveTo(currentZone,firstZone)) { - BoltLog.Info($"Formation {this} has not enough fuel for transition {currentZone} -> {path[0]}"); + BoltLog.Info($"Formation {this} has not enough fuel for transition {currentZone} -> {firstZone}"); return; } @@ -329,7 +319,7 @@ namespace GWConquest } } - pathQueue.SetEntries(path.Select(z => Zone.GetZoneId(z))); + pathQueue.SetEntries(zoneIDs); if(targetFormation != null) { @@ -342,7 +332,13 @@ namespace GWConquest public void BeginMovementServer(Zone targetZone, Formation targetFormation=null) { - BeginMovementServer(new Zone[] {targetZone}, targetFormation: targetFormation); + BeginMovementServer(new int[] {Zone.GetZoneId(targetZone)}, targetFormation: targetFormation); + } + + [ServerRpc] + public void BeginMovementServerRpc(int[] zoneIDs, NullableNetworkBehaviourReference targetFormation) + { + BeginMovementServer(zoneIDs, targetFormation: targetFormation.GetBehaviour()); } public void MoveToZone(Zone target) @@ -969,8 +965,7 @@ namespace GWConquest } } - var evnt = FormationDemoralizedAnimEvent.Create(entity); - evnt.Send(); + PlayFormationDemoralizedAnimation(); } } } @@ -1022,7 +1017,8 @@ namespace GWConquest } } - public override void OnEvent(FormationDemoralizedAnimEvent evnt) + [ClientRpc] + public void PlayFormationDemoralizedAnimation() { if(HeroUnit != null && HeroUnit.CurrentIcon != null) { @@ -1074,6 +1070,16 @@ namespace GWConquest } } + [ServerRpc] + public void AssignLeaderRpc(NullableNetworkBehaviourReference unit) + { + heroUnit.Value = unit; + } + + public IInventory GetInventory() + { + return FormationInventory; + } } diff --git a/Assets/GWConquest/Scripts/GWNetworkArray.cs b/Assets/GWConquest/Scripts/GWNetworkArray.cs index ecf72d6..531c5ff 100644 --- a/Assets/GWConquest/Scripts/GWNetworkArray.cs +++ b/Assets/GWConquest/Scripts/GWNetworkArray.cs @@ -1,5 +1,5 @@ using Unity.Netcode; namespace GWConquest { - + } \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/Inventory.cs b/Assets/GWConquest/Scripts/Inventory.cs index 2fe9ebb..5710e60 100644 --- a/Assets/GWConquest/Scripts/Inventory.cs +++ b/Assets/GWConquest/Scripts/Inventory.cs @@ -172,6 +172,11 @@ namespace GWConquest bool RemoveItem(ushort item, int amount); } + public interface IHasInventory + { + IInventory GetInventory(); + } + public class Inventory : GWNetworkList, IInventory { // Question: Can there be more then one half-full stack for an item type?? @@ -320,6 +325,25 @@ namespace GWConquest return dict.Values; } + public static void MoveItem(IInventory origin, IInventory target, ushort itemID, int itemAmount) + { + if(origin != null && target != null) + { + if(origin.HasItem(itemID, itemAmount) && target.DoesItemFit(itemID, itemAmount)) + { + origin.RemoveItem(itemID, itemAmount); + target.AddItem(itemID, itemAmount); + } + else { + BoltLog.Error("Could not move {0} of item {1} between inventories", itemAmount, itemID); + } + + } + else { + BoltLog.Error("One of the specified inventories is null"); + } + } + } public class CombinedInventory : IInventory diff --git a/Assets/GWConquest/Scripts/Player.cs b/Assets/GWConquest/Scripts/Player.cs index bd3a4c3..4267c26 100644 --- a/Assets/GWConquest/Scripts/Player.cs +++ b/Assets/GWConquest/Scripts/Player.cs @@ -161,6 +161,12 @@ namespace GWConquest } } } + + [ServerRpc] + public void BuildUnit(int zoneID, ushort unitClassID) + { + Unit.SpawnUnit(Zone.GetFromId(zoneID), UnitClass.FromID(unitClassID), this); + } } [System.Serializable] diff --git a/Assets/GWConquest/Scripts/ServerCallbacks.cs b/Assets/GWConquest/Scripts/ServerCallbacks.cs index b5da42a..cd18399 100644 --- a/Assets/GWConquest/Scripts/ServerCallbacks.cs +++ b/Assets/GWConquest/Scripts/ServerCallbacks.cs @@ -3,13 +3,25 @@ using UnityEngine; using UdpKit; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; namespace GWConquest { [BoltGlobalBehaviour(BoltNetworkModes.Server)] - public class ServerCallbacks : GlobalEventListener + public class ServerCallbacks : NetworkBehaviour { + private static ServerCallbacks _instance; + public static ServerCallbacks Instance { + get { + if(_instance == null) + { + _instance = FindObjectOfType(); + } + return _instance; + } + } + public override void SceneLoadLocalDone(string scene, IProtocolToken token) { if(scene != "GalaxyMap") @@ -91,93 +103,21 @@ namespace GWConquest } - public override void OnEvent(BuildUnitEvent evnt) - { - Zone zone = Zone.GetFromId(evnt.Zone); - var unitClass = UnitClass.FromName(evnt.UnitClass); - Player player = evnt.Player.GetComponent(); - - Unit.SpawnUnit(zone, unitClass, player); - - } - - public override void OnEvent(MoveFormationEvent evnt) - { - Formation formation = evnt.Formation.GetComponent(); - var targetFormation = evnt.TargetFormation?.GetComponent(); - var path = evnt.PathQueue == null ? null : (evnt.PathQueue as ZoneListToken).entries; - formation.BeginMovementServer(path, targetFormation: targetFormation); - } - - public override void OnEvent(AddProductionEvent evnt) - { - DistrictFactory factory = evnt.Factory.GetComponent(); - IBuildable buildable = evnt.IsUpgrade ? DistrictUpgrade.FromName(evnt.UnitClass) : UnitClass.FromName(evnt.UnitClass); - factory.AddProductionQueueEntry(buildable, evnt.Player.GetComponent()); - } - - public override void OnEvent(MoveUnitToFlankEvent evnt) - { - var flank = evnt.Flank.GetComponent(); - flank.Battle.MoveUnitToFlank(evnt.Unit.GetComponent(), flank, evnt.FlankIndex); - } - - public override void OnEvent(MoveUnitToReserveEvent evnt) - { - var unit = evnt.Unit.GetComponent(); - if(unit.CurrentBattle != null) - { - unit.CurrentBattle.MoveUnitToReserve(unit); - } - } - - public override void OnEvent(AssignLeaderEvent evnt) + [ServerRpc] + public void MoveItemRpc(NetworkBehaviourReference origin, NetworkBehaviourReference target, ushort itemID, int itemAmount) { - var formation = evnt.Formation.GetComponent(); - var unit = evnt.Unit?.GetComponent(); - formation.HeroUnit = unit; - } + NetworkBehaviour originBeh = origin.GetBehaviour(); + NetworkBehaviour targetBeh = target.GetBehaviour(); - public override void OnEvent(StartShellingEvent evnt) - { - var unit = evnt.Unit.GetComponent(); - var flank = evnt.BattleFlank?.GetComponent(); - unit.StartShelling(flank); - } - public override void OnEvent(MoveItemEvent evnt) - { - IInventory origin = null; - if(evnt.Origin.GetComponent() != null) + if(originBeh is IHasInventory && targetBeh is IHasInventory) { - origin = evnt.Origin.GetComponent().Inventory; - } - else if(evnt.Origin.GetComponent() != null) { - origin = evnt.Origin.GetComponent().FormationInventory; - } - - IInventory target = null; - if(evnt.Target.GetComponent() != null) - { - target = evnt.Target.GetComponent().Inventory; - } - else if(evnt.Target.GetComponent() != null) { - target = evnt.Target.GetComponent().FormationInventory; - } - - if(origin != null && target != null) - { - if(origin.HasItem(evnt.ItemName, evnt.ItemAmount) && target.DoesItemFit(evnt.ItemName, evnt.ItemAmount)) - { - origin.RemoveItem(evnt.ItemName, evnt.ItemAmount); - target.AddItem(evnt.ItemName, evnt.ItemAmount); - } - else { - BoltLog.Error("Could not move {0} of item {1} between inventories", evnt.ItemAmount, evnt.ItemName); - } + IInventory originInv = (originBeh as IHasInventory).GetInventory(); + IInventory targetInv = (targetBeh as IHasInventory).GetInventory(); + Inventory.MoveItem(originInv, targetInv, itemID, itemAmount); } else { - BoltLog.Error("One of the specified inventories is null"); + BoltLog.Error("One of the specified behaviours is not an inventory"); } } } diff --git a/Assets/GWConquest/Scripts/UI/BattleUnitIcon.cs b/Assets/GWConquest/Scripts/UI/BattleUnitIcon.cs index a9b32f5..e686884 100644 --- a/Assets/GWConquest/Scripts/UI/BattleUnitIcon.cs +++ b/Assets/GWConquest/Scripts/UI/BattleUnitIcon.cs @@ -259,11 +259,11 @@ namespace GWConquest { } - public void PlayHitAnim(UnitDamageAnimEvent evnt) + public void PlayHitAnim(Unit attacker) { if(IsAnimatorIdle) { - bool explode = (evnt.Attacker != null && evnt.Attacker.GetComponent().Class.UnitType == UnitType.Artillery) + bool explode = (attacker != null && attacker.Class.UnitType == UnitType.Artillery) || Unit.Class.UnitType != UnitType.Infantry; if(!explode) { @@ -297,9 +297,9 @@ namespace GWConquest { SoundEffects.Instance.PlayEffect("ShieldsDamage"); } - public void PlayDeathAnim(UnitDamageAnimEvent evnt) + public void PlayDeathAnim(Unit attacker) { - bool explode = (evnt.Attacker != null && evnt.Attacker.GetComponent().Class.UnitType == UnitType.Artillery) + bool explode = (attacker != null && attacker.Class.UnitType == UnitType.Artillery) || Unit.Class.UnitType != UnitType.Infantry; StartCoroutine(DeathAnimCoroutine(explode)); } diff --git a/Assets/GWConquest/Scripts/Unit.cs b/Assets/GWConquest/Scripts/Unit.cs index f5e30de..4e770da 100644 --- a/Assets/GWConquest/Scripts/Unit.cs +++ b/Assets/GWConquest/Scripts/Unit.cs @@ -344,15 +344,28 @@ namespace GWConquest Formation.TakeMoraleDamage((Hitpoints <= 0 ? Class.Morale : remainingDamage * moraleFactor) * flankFactor); } - var evnt = UnitDamageAnimEvent.Create(entity); - evnt.Attacker = attacker.entity; - evnt.IsDead = State.Hitpoints <= 0; - evnt.IsDemoralized = IsDemoralized; - evnt.ShieldDamage = Shields > 0; - evnt.ShieldBroken = shieldBroken; - evnt.GlancingHit = isGlancingHit; - evnt.WeaponType = (int) weaponType; - evnt.Send(); + DamageAnimationType animType = DamageAnimationType.Damage; + if(Hitpoints <= 0) + { + animType = DamageAnimationType.Death; + } + else if(IsDemoralized) + { + animType = DamageAnimationType.Demoralized; + } + else if(shieldBroken) + { + animType = DamageAnimationType.ShieldBroken; + } + else if(Shields > 0) + { + animType = DamageAnimationType.ShieldDamage; + } + else if(isGlancingHit) + { + animType = DamageAnimationType.GlancingHit; + } + PlayUnitDamageAnimation(weaponType, animType, attacker); if(Hitpoints <= 0) { @@ -364,41 +377,44 @@ namespace GWConquest } } - public override void OnEvent(UnitDamageAnimEvent evnt) + public enum DamageAnimationType + { + Death, Damage, Demoralized, ShieldDamage, ShieldBroken, GlancingHit + } + + [ClientRpc] + public void PlayUnitDamageAnimation(WeaponType weaponType, DamageAnimationType animationType, NullableNetworkBehaviourReference attacker) { if(IconEnabled) { - WeaponType weaponType = (WeaponType)evnt.WeaponType; - if (evnt.IsDead) + switch(animationType) { - CurrentIcon.PlayDeathAnim(evnt); - } - else if(evnt.IsDemoralized && !DemoralizedAnimPlayed) - { - CurrentIcon.PlayDemoralizedAnim(); - DemoralizedAnimPlayed = true; - } - else if(evnt.ShieldBroken) - { - CurrentIcon.PlayShieldBreakAnim(); - } - else if(evnt.ShieldDamage) - { - CurrentIcon.PlayShieldDamageAnim(); - } - else if(evnt.GlancingHit) - { - CurrentIcon.PlayGlancingHitAnim(weaponType == WeaponType.Light || weaponType == WeaponType.Ship); - } - else { - CurrentIcon.PlayHitAnim(evnt); + case DamageAnimationType.Death: + CurrentIcon.PlayDeathAnim(attacker.GetBehaviour()); + break; + case DamageAnimationType.Demoralized: + CurrentIcon.PlayDemoralizedAnim(); + DemoralizedAnimPlayed = true; + break; + case DamageAnimationType.ShieldBroken: + CurrentIcon.PlayShieldBreakAnim(); + break; + case DamageAnimationType.ShieldDamage: + CurrentIcon.PlayShieldDamageAnim(); + break; + case DamageAnimationType.GlancingHit: + CurrentIcon.PlayGlancingHitAnim(weaponType == WeaponType.Light || weaponType == WeaponType.Ship); + break; + case DamageAnimationType.Damage: + CurrentIcon.PlayHitAnim(attacker.GetBehaviour()); + break; } } } - public override void OnEvent(UnitAttackingEvent evnt) + public void PlayUnitAttackingAnimation(NullableNetworkBehaviourReference target, bool hit, WeaponType weaponType) { if(IconEnabled) { @@ -406,15 +422,13 @@ namespace GWConquest if(Class.UnitType == UnitType.Artillery) { - SoundEffects.Instance.PlayEffect("ArtilleryShot", volumeMultilpier: evnt.Hit? 1f: 0.5f); + SoundEffects.Instance.PlayEffect("ArtilleryShot", volumeMultilpier: hit? 1f: 0.5f); } else { - var target = evnt.Target?.GetComponent(); - WeaponType weaponType = (WeaponType)evnt.WeaponType; - if(target != null && evnt.Hit) + if(target.HasValue && hit) { - CurrentIcon.GetComponentInParent().SpawnShotEffect(CurrentIcon, target.CurrentIcon, weaponType); + CurrentIcon.GetComponentInParent().SpawnShotEffect(CurrentIcon, target.GetBehaviour().CurrentIcon, weaponType); } else { CurrentIcon.GetComponentInParent().SpawnShotEffect(CurrentIcon, null, weaponType); @@ -423,15 +437,14 @@ namespace GWConquest } else if(Class.ZoneType == ZoneType.Space && IngameUI.PlanetViewEnabled) { - var target = evnt.Target?.GetComponent(); - if(evnt.Hit && target != null) + if(target.HasValue && hit) { var planetView = IngameUI.Instance.PlanetView; if(planetView.selectedPlanet == Formation.currentZone.planet) { if(planetView.FleetIcons.isActiveAndEnabled) { - planetView.FleetIcons.PlayShotEffect(this, target); + planetView.FleetIcons.PlayShotEffect(this, target.GetBehaviour()); } } } @@ -547,6 +560,12 @@ namespace GWConquest SetActionCooldown(GameManager.Instance.ArtilleryCooldown / movement); } + [ServerRpc] + public void StartShellingRpc(NetworkBehaviourReference target) + { + StartShelling(target.GetBehaviour()); + } + public float MoraleDamageFactor { get => 0.8f; } @@ -624,4 +643,6 @@ namespace GWConquest } } + + } \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/Util.cs b/Assets/GWConquest/Scripts/Util.cs index 77dd55f..9c11a7f 100644 --- a/Assets/GWConquest/Scripts/Util.cs +++ b/Assets/GWConquest/Scripts/Util.cs @@ -323,6 +323,17 @@ namespace GWConquest } } + public static NetworkBehaviour GetBehaviour(this NetworkBehaviourReference reference) + { + if(reference.TryGet(out NetworkBehaviour beh)) + { + return beh; + } + else { + return null; + } + } + } } \ No newline at end of file