From ca6937c2ac906d82f805dc7215071ead422ccece Mon Sep 17 00:00:00 2001 From: Laurids Jeppe Date: Sun, 16 Jul 2023 23:49:28 +0200 Subject: [PATCH] (WIP) Updating to Netcode Part 1 --- Assets/GWConquest/Scripts/Battle.cs | 87 ++--- Assets/GWConquest/Scripts/BattleFlank.cs | 75 +++-- Assets/GWConquest/Scripts/District.cs | 110 ++++--- Assets/GWConquest/Scripts/DistrictFactory.cs | 124 ++++--- Assets/GWConquest/Scripts/DistrictUpgrade.cs | 27 ++ Assets/GWConquest/Scripts/Formation.cs | 302 +++++++++++------- Assets/GWConquest/Scripts/GWNetworkArray.cs | 5 + .../GWConquest/Scripts/GWNetworkArray.cs.meta | 11 + Assets/GWConquest/Scripts/GWNetworkList.cs | 37 +++ .../GWConquest/Scripts/GWNetworkList.cs.meta | 11 + Assets/GWConquest/Scripts/GWNullable.cs | 74 +++++ Assets/GWConquest/Scripts/GWNullable.cs.meta | 11 + Assets/GWConquest/Scripts/Inventory.cs | 146 +++++---- Assets/GWConquest/Scripts/ItemRegistry.cs | 37 +++ Assets/GWConquest/Scripts/Planet.cs | 92 +++--- Assets/GWConquest/Scripts/Player.cs | 117 ++++--- Assets/GWConquest/Scripts/UID.cs | 44 +++ Assets/GWConquest/Scripts/UID.cs.meta | 11 + Assets/GWConquest/Scripts/Unit.cs | 187 ++++++----- Assets/GWConquest/Scripts/UnitClass.cs | 34 +- Assets/GWConquest/Scripts/Util.cs | 12 + Packages/manifest.json | 1 + Packages/packages-lock.json | 38 +++ 23 files changed, 1049 insertions(+), 544 deletions(-) create mode 100644 Assets/GWConquest/Scripts/GWNetworkArray.cs create mode 100644 Assets/GWConquest/Scripts/GWNetworkArray.cs.meta create mode 100644 Assets/GWConquest/Scripts/GWNetworkList.cs create mode 100644 Assets/GWConquest/Scripts/GWNetworkList.cs.meta create mode 100644 Assets/GWConquest/Scripts/GWNullable.cs create mode 100644 Assets/GWConquest/Scripts/GWNullable.cs.meta create mode 100644 Assets/GWConquest/Scripts/UID.cs create mode 100644 Assets/GWConquest/Scripts/UID.cs.meta diff --git a/Assets/GWConquest/Scripts/Battle.cs b/Assets/GWConquest/Scripts/Battle.cs index c42052f..41e66cb 100644 --- a/Assets/GWConquest/Scripts/Battle.cs +++ b/Assets/GWConquest/Scripts/Battle.cs @@ -3,19 +3,33 @@ using System.Collections.Generic; using System.Linq; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; +using Unity.Collections; namespace GWConquest { - public class Battle : GWBoltBehaviour + public class Battle : NetworkBehaviour { - public EntityList FormationList; + private GWNetworkList formations; + private NetworkVariable zoneID; + private NetworkVariable battleName; + private GWNetworkList flanks; + private NetworkVariable flankCount; + private NetworkVariable isOver; + private NetworkVariable victorID; + private NetworkVariable preparingCooldown; + + public IEnumerable Formations + { + get => formations.Select(r => r.GetBehaviour()); + } public IEnumerable AllPlayers { - get => FormationList.Select((f,i) => f.Entity.GetComponent().Player).Distinct(); + get => Formations.Select(f => f.Player).Distinct(); } public IEnumerable AllUnits { - get => FormationList.SelectMany(e => e.Entity.GetComponent().Units); + get => Formations.SelectMany(f => f.Units); } private List UnitActions = new List(); @@ -68,30 +82,30 @@ namespace GWConquest } public int FlankCount { - get => State.FlankCount; - set => State.FlankCount = value; + get => flankCount.Value; + set => flankCount.Value = value; } - public void SetFlank(int index, BattleFlank flank) + public void SetFlank(ushort index, BattleFlank flank) { flank.FlankId = index; - State.Flanks[index] = flank?.entity; + flanks[index] = flank; } - public BattleFlank GetFlank(int index) + public BattleFlank GetFlank(ushort index) { - return State.Flanks[index].GetComponent(); + return flanks[index].GetBehaviour(); } public IEnumerable AllFlanks { - get => State.Flanks.Where(e => e != null).Select(e => e.GetComponent()); + get => flanks.Select(r => r.GetBehaviour()); } public Zone Zone { - get => Zone.GetFromId(State.Zone); + get => Zone.GetFromId(zoneID.Value); set { - State.Zone = Zone.GetZoneId(value); + zoneID.Value = Zone.GetZoneId(value); transform.position = value.transform.position; } } @@ -101,29 +115,32 @@ namespace GWConquest } public bool IsOver { - get => State.IsOver; - set => State.IsOver = value; + get => isOver.Value; + set => isOver.Value = value; } public float PreparingCooldown { - get => State.PreparingCooldown; - set => State.PreparingCooldown = value; + get => preparingCooldown.Value; + set => preparingCooldown.Value = value; } public bool IsInPreparing { get => PreparingCooldown > 0; } - public override void Attached() + private void OnZoneChanged(int oldID, int newID) + { + if(newID != -1) + { + Zone.GetFromId(newID).CurrentBattle = this; + } + } + + public override void OnNetworkSpawn() { - FormationList = new EntityList(State, "FormationList"); + zoneID.OnValueChanged += OnZoneChanged; - State.AddCallback("Zone", () => { - if(State.Zone != -1) - { - Zone.CurrentBattle = this; - } - }); + base.OnNetworkSpawn(); } private BattleFlank InstantiateNewFlank(int maxUnitCount) @@ -138,13 +155,13 @@ namespace GWConquest public void Init() { PreparingCooldown = GameManager.Instance.BattlePreparingCooldown; - State.FlankCount = Zone.zoneType == ZoneType.Ground ? 6 : 2; + flankCount.Value = Zone.zoneType == ZoneType.Ground ? 6 : 2; int maxUnitPerFlank = Zone.zoneType == ZoneType.Ground ? 9 : 20; var allPlayers = AllPlayers.ToList(); if(allPlayers.Count() >= 2) { - for(int i = 0; i < State.FlankCount; i += 2) + for(int i = 0; i < flankCount.Value; i += 2) { var flank1 = InstantiateNewFlank(maxUnitPerFlank); var flank2 = InstantiateNewFlank(maxUnitPerFlank); @@ -155,8 +172,8 @@ namespace GWConquest flank1.Player = allPlayers[0]; flank2.Player = allPlayers[1]; - SetFlank(i, flank1); - SetFlank(i+1, flank2); + SetFlank((ushort) i, flank1); + SetFlank((ushort) (i+1), flank2); } } else { @@ -166,7 +183,7 @@ namespace GWConquest public void AddFormation(Formation f) { - FormationList.Add(f.entity); + formations.Add(f); f.OnAddedToBattle(this); @@ -175,7 +192,7 @@ namespace GWConquest public void RemoveFormation(Formation f) { - FormationList.Remove(f.entity); + formations.Remove(f); foreach(Unit u in f.Units) { @@ -200,10 +217,8 @@ namespace GWConquest } } - public override void SimulateOwner() + public void FixedUpdate() { - base.SimulateOwner(); - if(IsInPreparing) { PreparingCooldown -= BoltNetwork.FrameDeltaTime; @@ -250,10 +265,10 @@ namespace GWConquest var victoriousPlayer = AllPlayers.FirstOrDefault(); if(victoriousPlayer != null) { - State.VictorID = victoriousPlayer.PlayerId; + victorID.Value = victoriousPlayer.PlayerId; } else { - State.VictorID = -1; + victorID.Value = -1; } foreach(Unit unit in AllUnits) diff --git a/Assets/GWConquest/Scripts/BattleFlank.cs b/Assets/GWConquest/Scripts/BattleFlank.cs index b4a387c..57f7b4d 100644 --- a/Assets/GWConquest/Scripts/BattleFlank.cs +++ b/Assets/GWConquest/Scripts/BattleFlank.cs @@ -2,11 +2,21 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; using Photon.Bolt; +using Unity.Netcode; namespace GWConquest { - public class BattleFlank : GWBoltBehaviour + public class BattleFlank : NetworkBehaviour { + private NetworkVariable battle; + private NetworkVariable flankID; + private NetworkVariable maxUnitCount; + private NetworkVariable playerID; + private GWNetworkList units; + private NetworkVariable opposingFlank; + private GWNetworkList deathCooldowns; + + public const int SlotsPerRowGround = 3; public const int SlotsPerRowSpace = 2; @@ -14,61 +24,54 @@ namespace GWConquest { public BattleFlankUI CurrentUI; public Battle Battle { - get => BoltEntityCache.Get(State.Battle); - set => State.Battle = BoltEntityCache.Set(value); + get => battle.Value.GetBehaviour(); + set => battle.Value = value; } - public int FlankId { - get => State.FlankId; - set => state.FlankId = value; + public ushort FlankId { + get => flankID.Value; + set => flankID.Value = value; } public bool IsSpace => Battle.IsSpaceBattle; public int MaxUnitCount { - get => State.MaxUnitCount; - set => State.MaxUnitCount = value; + get => maxUnitCount.Value; + set => maxUnitCount.Value = value; } public BattleFlank OpposingFlank { - get => BoltEntityCache.Get(State.OpposingFlank); - set => State.OpposingFlank = BoltEntityCache.Set(value); + get => opposingFlank.Value.GetBehaviour(); + set => opposingFlank.Value = value; } public Player Player { - get => BoltEntityCache.Get(State.Player); - set => State.Player = BoltEntityCache.Set(value); + get => Player.GetPlayerById(playerID.Value); + set => playerID.Value = value.PlayerId; } public Unit GetUnit(int index) { - return State.Units[index]?.GetComponent(); + return units[index].GetBehaviour(); } public void SetUnit(int index, Unit unit) { - State.Units[index] = unit?.entity; + units[index] = unit; } public float GetDeathCooldown(int index) { - return State.DeathCooldowns[index]; + return deathCooldowns[index]; } public void SetDeathCooldown(int index, float cooldown) { - State.DeathCooldowns[index] = cooldown; + deathCooldowns[index] = cooldown; } public int GetUnitIndex(Unit unit) { - for(int i = 0; i < State.MaxUnitCount; i++) - { - if(State.Units[i] == unit.entity) - { - return i; - } - } - return -1; + return units.IndexOf(unit); } public int GetRowFromIndex(int index) @@ -101,7 +104,7 @@ namespace GWConquest { public bool IsBroken { - get => State.Units.Count(e => e != null) == 0; + get => units.Count(r => r.HasValue) == 0; } public void RemoveUnit(Unit unit) @@ -115,17 +118,17 @@ namespace GWConquest { public IEnumerable Units { get { - return State.Units.Where(e => e != null).Select((e,i) => e.GetComponent()); + return units.Where(r => r.HasValue).Select(r => r.GetBehaviour()); } } - public override void SimulateOwner() + public void FixedUpdate() { for(int i = 0; i < MaxUnitCount; i++) { if(GetDeathCooldown(i) > 0) { - State.DeathCooldowns[i] -= BoltNetwork.FrameDeltaTime; + deathCooldowns[i] -= BoltNetwork.FrameDeltaTime; if(GetDeathCooldown(i) <= 0) { var unit = GetUnit(i); @@ -138,6 +141,22 @@ namespace GWConquest { } } } + + public override void OnNetworkSpawn() + { + maxUnitCount.OnValueChanged += (oldVal, newVal) => { + units.Clear(); + deathCooldowns.Clear(); + + for(int i = 0; i < newVal; i++) + { + units.Add(null); + deathCooldowns.Add(0f); + } + }; + + base.OnNetworkSpawn(); + } } } \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/District.cs b/Assets/GWConquest/Scripts/District.cs index c4b3b06..d141d45 100644 --- a/Assets/GWConquest/Scripts/District.cs +++ b/Assets/GWConquest/Scripts/District.cs @@ -3,14 +3,30 @@ using System.Collections.Generic; using System.Linq; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; +using Unity.Collections; namespace GWConquest { - public class District : GWBoltBehaviour + public class District : NetworkBehaviour { public static List AllDistricts = new List(); public static List AllConnections = new List(); + private NetworkVariable storageCapacity; + private NetworkVariable controllingPlayerID; + public Inventory Inventory; + private NetworkVariable itemProductionCooldown; + private NetworkVariable zoneID; + private NetworkVariable planet; + private NetworkVariable level; + private NetworkVariable districtType; + private NetworkVariable districtName; + private NetworkVariable relativePosition; + private GWNetworkList connectedDistricts; + private GWNetworkList upgrades; + private NetworkVariable captureCooldown; + public bool DistrictStatic; public Sprite DefaultSprite; @@ -19,8 +35,6 @@ namespace GWConquest public DistrictType DistrictType = DistrictType.Civil; public int DistrictLevel; - public EntityList ConnectionList; - public StringList UpgradeList; private Zone _zone; public Zone Zone { @@ -42,7 +56,7 @@ namespace GWConquest public Player ControllingPlayer { get { - var id = State.ControllingPlayerId; + var id = controllingPlayerID.Value; if(id < 0) { return null; @@ -54,66 +68,61 @@ namespace GWConquest } else { - return Player.GetPlayerById(State.ControllingPlayerId); + return Player.GetPlayerById(controllingPlayerID.Value); } } set { if(value == null) { - State.ControllingPlayerId = -1; + controllingPlayerID.Value = -1; } else { - State.ControllingPlayerId = value.PlayerId; + controllingPlayerID.Value = value.PlayerId; } } } - - public Inventory Inventory; public int StorageCapacity { - get => State.StorageCapacity; - set => State.StorageCapacity = value; + get => storageCapacity.Value; + set => storageCapacity.Value = value; } - public override void Attached() + public override void OnNetworkSpawn() { - ConnectionList = new EntityList(State, "ConnectedDistricts"); - UpgradeList = new StringList(State, "Upgrades"); - if(entity.IsOwner) + if(IsOwner) { StorageCapacity = GameManager.Instance.DefaultStorageCapacity; - State.ControllingPlayerId = -1; + controllingPlayerID.Value = -1; } - Inventory = new Inventory(State, "Inventory"); Inventory.StorageCapacity = StorageCapacity; - State.AddCallback("StorageCapacity", () => { - Inventory.StorageCapacity = StorageCapacity; - }); + storageCapacity.OnValueChanged += (prev, now) => Inventory.StorageCapacity = now; //BoltLog.Info("Setting storage capacity to {0}", StorageCapacity); AllDistricts.Add(this); if(DistrictStatic) { - if(entity.IsOwner) + if(IsOwner) { - State.ZoneID = Zone.GetZoneId(GetComponent()); - State.DistrictType = (int) DistrictType; - State.DistrictName = DistrictName; - State.Level = DistrictLevel; + zoneID.Value = Zone.GetZoneId(GetComponent()); + districtType.Value = DistrictType; + districtName.Value = DistrictName; + level.Value = DistrictLevel; } Planet = GetComponentInParent(); } + + base.OnNetworkSpawn(); } public void InitNonStatic() { - Planet = BoltEntityCache.Get(State.Planet); + Planet = planet.Value.GetBehaviour(); if(DistrictType == DistrictType.SpaceStation) { @@ -125,22 +134,22 @@ namespace GWConquest Util.ArrayAppend(ref Planet.groundZones, Zone); } - if(entity.IsOwner) + if(IsOwner) { - State.ZoneID = Zone.GetZoneId(GetComponent()); - State.DistrictType = (int) DistrictType; - State.DistrictName = DistrictName; - State.Level = DistrictLevel; - State.RelativePosition = transform.localPosition; + zoneID.Value = Zone.GetZoneId(GetComponent()); + districtType.Value = DistrictType; + districtName.Value = DistrictName; + level.Value = DistrictLevel; + relativePosition.Value = transform.localPosition; } else { - Zone.SetZoneId(State.ZoneID); - DistrictType = (DistrictType) State.DistrictType; - DistrictName = State.DistrictName; - DistrictLevel = state.Level; + Zone.SetZoneId(zoneID.Value); + DistrictType = districtType.Value; + DistrictName = districtName.Value.ToString(); + DistrictLevel = level.Value; transform.SetParent(Planet.transform, false); - transform.localPosition = State.RelativePosition; + transform.localPosition = relativePosition.Value; gameObject.name = DistrictName; } @@ -149,9 +158,9 @@ namespace GWConquest public void InitConnections() { - foreach(var ent in ConnectionList) + foreach(var reference in connectedDistricts) { - var otherDistrict = ent.Entity.GetComponent(); + var otherDistrict = reference.GetBehaviour(); if(!AllConnections.Exists(c => c.district1 == this && c.district2 == otherDistrict || c.district2 == this && c.district1 == otherDistrict)) { var go = new GameObject("District Connection"); @@ -252,17 +261,17 @@ namespace GWConquest { if(Zone.Formations.Where(f => f.MovementState == FormationMovementState.Idle).FirstOrDefault() != null) { - if(State.CaptureCooldown >= GameManager.Instance.DistrictCaptureCooldown) + if(captureCooldown.Value >= GameManager.Instance.DistrictCaptureCooldown) { BoltLog.Info($"Allegiance changed in district {this} to player {player}"); ControllingPlayer = player; } else { - if(State.CaptureCooldown == 0) + if(captureCooldown.Value == 0) { BoltLog.Info("Player {0} is the only player in district {1}, starting allegiance change...", player, this); } - State.CaptureCooldown += BoltNetwork.FrameDeltaTime; + captureCooldown.Value += BoltNetwork.FrameDeltaTime; } return; } @@ -270,10 +279,10 @@ namespace GWConquest } } - State.CaptureCooldown = 0; + captureCooldown.Value = 0; } - public override void SimulateOwner() + public void FixedUpdate() { CheckControllingChange(); @@ -284,11 +293,11 @@ namespace GWConquest bool producesCredits = DistrictType == DistrictType.Civil; if(producingItem != null || producesCredits) { - if(State.ItemProductionCooldown <= 0f) + if(itemProductionCooldown.Value <= 0f) { if(producingItem != null) { - Inventory.AddItem(producingItem, 1); + Inventory.AddItem(ItemRegistry.Instance.IDFromName(producingItem), 1); } if(producesCredits) @@ -296,10 +305,10 @@ namespace GWConquest ControllingPlayer.Credits++; } - State.ItemProductionCooldown = ProducingCooldown; + itemProductionCooldown.Value = ProducingCooldown; } else { - State.ItemProductionCooldown -= BoltNetwork.FrameDeltaTime; + itemProductionCooldown.Value -= Time.fixedDeltaTime; } } } @@ -319,6 +328,11 @@ namespace GWConquest return player != null && cost.AvailableIn(accesibleItems, player); } + public void AddDistrictUpgrade(ushort upgradeID) + { + upgrades.Add(upgradeID); + } + } public enum DistrictType diff --git a/Assets/GWConquest/Scripts/DistrictFactory.cs b/Assets/GWConquest/Scripts/DistrictFactory.cs index f5650ba..f64d59d 100644 --- a/Assets/GWConquest/Scripts/DistrictFactory.cs +++ b/Assets/GWConquest/Scripts/DistrictFactory.cs @@ -3,11 +3,44 @@ using System.Collections.Generic; using System.Linq; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; +using System; namespace GWConquest { - public class DistrictFactory : GWBoltBehaviour + public struct ProductionQueueEntry : INetworkSerializeByMemcpy, IEquatable { + public ushort UnitClass; + public float TimeProduced; + public ushort PlayerID; + public bool IsUpgrade; + + public Player Player { + get => Player.GetPlayerById(PlayerID); + } + + public bool Equals(ProductionQueueEntry other) + { + return UnitClass == other.UnitClass && + TimeProduced == other.TimeProduced && + PlayerID == other.PlayerID && + IsUpgrade == other.IsUpgrade; + } + + public ProductionQueueEntry IncreaseTime(float deltaTime) + { + return new ProductionQueueEntry() { + UnitClass = UnitClass, + PlayerID = PlayerID, + IsUpgrade = IsUpgrade, + TimeProduced = TimeProduced + deltaTime + }; + } + } + public class DistrictFactory : NetworkBehaviour + { + public GWNetworkList productionQueue; + public bool StartsBroken = false; public string[] SpecialUnits; @@ -34,52 +67,34 @@ namespace GWConquest return classes.Union(special); } - public int MaxQueueLength - { - get - { - return State.ProductionQueue.Length; - } - } - public int ProductionQueueLength { - get - { - for(int i = 0; i < MaxQueueLength; i++) - { - if(Util.IsStringEmpty(State.ProductionQueue[i].UnitClass)) - { - return i; - } - } - return MaxQueueLength; - } + get => productionQueue.Count; } public bool IsProducing { get { - return !Util.IsStringEmpty(State.ProductionQueue[0].UnitClass); + return ProductionQueueLength > 0; } } public IBuildable GetProductionQueueEntry(int i) { - if(State.ProductionQueue[i].IsUpgrade) + if(productionQueue[i].IsUpgrade) { - return DistrictUpgrade.FromName(State.ProductionQueue[i].UnitClass); + return DistrictUpgrade.FromID(productionQueue[i].UnitClass); } else { - return UnitClass.FromName(State.ProductionQueue[i].UnitClass); + return UnitClass.FromID(productionQueue[i].UnitClass); } } public float GetProductionTime(int i) { - return State.ProductionQueue[i].TimeProduced; + return productionQueue[i].TimeProduced; } public float GetProductionPercentage(int i) @@ -94,30 +109,7 @@ namespace GWConquest public void RemoveProductionQueueEntry(int index) { - if(index == MaxQueueLength - 1) - { - State.ProductionQueue[index].UnitClass = null; - State.ProductionQueue[index].TimeProduced = 0f; - State.ProductionQueue[index].Player = null; - } - else - { - for (int i = index; i < MaxQueueLength - 1; i++) - { - var nextEntry = State.ProductionQueue[i + 1]; - State.ProductionQueue[i].UnitClass = nextEntry.UnitClass; - State.ProductionQueue[i].TimeProduced = nextEntry.TimeProduced; - State.ProductionQueue[i].Player = nextEntry.Player; - if (Util.IsStringEmpty(nextEntry.UnitClass)) - { - return; - } - } - State.ProductionQueue[MaxQueueLength - 1].UnitClass = null; - State.ProductionQueue[MaxQueueLength - 1].TimeProduced = 0f; - State.ProductionQueue[MaxQueueLength - 1].Player = null; - } - + productionQueue.RemoveAt(index); } public void AddProductionQueueEntry(IBuildable buildable, Player player) @@ -137,18 +129,19 @@ namespace GWConquest foreach(var cost in buildable.BuildCost.Costs) { + var itemId = ItemRegistry.Instance.IDFromName(cost.Item); if(cost.Item == "Credits") { player.Credits -= cost.Amount; } else { - if(districtInv.HasItem(cost.Item, cost.Amount)) + if(districtInv.HasItem(itemId, cost.Amount)) { - districtInv.RemoveItem(cost.Item, cost.Amount); + districtInv.RemoveItem(itemId, cost.Amount); } - else if(!contested && planetInv.HasItem(cost.Item, cost.Amount)) + else if(!contested && planetInv.HasItem(itemId, cost.Amount)) { - planetInv.RemoveItem(cost.Item, cost.Amount); + planetInv.RemoveItem(itemId, cost.Amount); } else { BoltLog.Error("Error while building {0}: Item {1} x {2} not found in inventories", buildable, cost.Item, cost.Amount); @@ -159,14 +152,15 @@ namespace GWConquest } int length = ProductionQueueLength; - if(length < MaxQueueLength) - { - BoltLog.Info("Adding unit {0} to production queue on district {1}", buildable, gameObject.name); - State.ProductionQueue[length].UnitClass = buildable.ShortName; - State.ProductionQueue[length].TimeProduced = 0f; - State.ProductionQueue[length].Player = player.entity; - State.ProductionQueue[length].IsUpgrade = buildable is DistrictUpgrade; - } + BoltLog.Info("Adding unit {0} to production queue on district {1}", buildable, gameObject.name); + + productionQueue.Add(new ProductionQueueEntry() { + UnitClass = buildable.ID, + TimeProduced = 0f, + PlayerID = player.PlayerId, + IsUpgrade = buildable is DistrictUpgrade + }); + } public float GetBuildTimeForBuildable(IBuildable buildable) @@ -174,11 +168,11 @@ namespace GWConquest return buildable.BuildTime * GameManager.Instance.TimeScale; } - public override void SimulateOwner() + public void FixedUpdate() { if(IsProducing) { - State.ProductionQueue[0].TimeProduced += BoltNetwork.FrameDeltaTime; + productionQueue[0] = productionQueue[0].IncreaseTime(Time.fixedDeltaTime); IBuildable buildable = GetProductionQueueEntry(0); if (DebugUI.FreeBuild || GetProductionTime(0) >= GetBuildTimeForBuildable(buildable)) { @@ -187,7 +181,7 @@ namespace GWConquest Zone zone = GetComponent(); if(zone != null) { - Unit.SpawnUnit(zone, buildable as UnitClass, State.ProductionQueue[0].Player.GetComponent()); + Unit.SpawnUnit(zone, buildable as UnitClass, productionQueue[0].Player); } else { @@ -196,7 +190,7 @@ namespace GWConquest } else if(buildable is DistrictUpgrade) { - District.UpgradeList.Add((buildable as DistrictUpgrade).ShortName); + District.AddDistrictUpgrade(buildable.ID); } RemoveProductionQueueEntry(0); } diff --git a/Assets/GWConquest/Scripts/DistrictUpgrade.cs b/Assets/GWConquest/Scripts/DistrictUpgrade.cs index 1a2cb5b..48f1a7d 100644 --- a/Assets/GWConquest/Scripts/DistrictUpgrade.cs +++ b/Assets/GWConquest/Scripts/DistrictUpgrade.cs @@ -8,16 +8,20 @@ namespace GWConquest public class DistrictUpgrade : IBuildable { + private static DistrictUpgrade[] Upgrades; private static Dictionary UpgradeRegistry; public static void LoadRegistry() { BoltLog.Info("Loading district upgrades..."); + List upgradeList = new List(); UpgradeRegistry = new Dictionary(); TextAsset database = Resources.Load("Database/DistrictUpgrades"); CSVFile csv = CSVFile.ParseCSV(database.text); + ushort nextID = 0; + for (int i = 0; i < csv.Length; i++) { try @@ -73,6 +77,10 @@ namespace GWConquest upgrade.LoadTexture(); + upgrade.ID = nextID; + nextID++; + + upgradeList.Add(upgrade); UpgradeRegistry.Add(upgrade.ShortName, upgrade); } catch (Exception ex) @@ -82,9 +90,27 @@ namespace GWConquest } } + Upgrades = upgradeList.ToArray(); + BoltLog.Info("District upgrades loaded."); } + public static DistrictUpgrade FromID(ushort id) + { + if(Upgrades == null) + { + LoadRegistry(); + } + if(id < Upgrades.Length) + { + return Upgrades[id]; + } + else { + Debug.LogWarning($"Unknown district id: {id}"); + return null; + } + } + public static DistrictUpgrade FromName(string name) { if(UpgradeRegistry == null) @@ -117,6 +143,7 @@ namespace GWConquest } + public ushort ID {get; private set;} public string ShortName {get; private set;} public string FullName {get; private set;} public Sprite Sprite {get; private set;} diff --git a/Assets/GWConquest/Scripts/Formation.cs b/Assets/GWConquest/Scripts/Formation.cs index 0aa8f99..2dded86 100644 --- a/Assets/GWConquest/Scripts/Formation.cs +++ b/Assets/GWConquest/Scripts/Formation.cs @@ -5,62 +5,101 @@ using System.Collections.Generic; using System.Collections; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; namespace GWConquest { - public class Formation : GWBoltEntityListener, IMovable + public struct Transition : INetworkSerializeByMemcpy + { + public int OriginZoneID; + public int TargetZoneID; + public float TransitionLength; + public bool IsCurved; + + public Zone OriginZone { + get => Zone.GetFromId(OriginZoneID); + } + public Zone TargetZone { + get => Zone.GetFromId(TargetZoneID); + } + } + + + public class Formation : NetworkBehaviour, IMovable { public static List AllFormations = new List(); - private Zone lastZone; + private NetworkVariable currentZoneID; + private NetworkVariable isSpace; + private NetworkVariable currentTransition; + private NetworkVariable player; + private GWNetworkList units; + private GWNetworkList pathQueue; + private NetworkVariable currentBattle; + private NetworkVariable heroUnit; + private GWNetworkList subFormations; + private NetworkVariable formationName; + private NetworkVariable formationNumber; + private NetworkVariable movementTargetFormation; + private NetworkVariable movementOriginFormation; + private NetworkVariable isEmbarked; + private NetworkVariable parentFormation; + private NetworkVariable morale; + private NetworkVariable startingMorale; + private NetworkVariable movementState; + private NetworkVariable actionCooldown; + private NetworkVariable actionCooldownMax; + private NetworkVariable fleetRestingPoint; + + public Zone currentZone { get { - return Zone.GetFromId(State.CurrentZone); + return Zone.GetFromId(currentZoneID.Value); } set { - State.CurrentZone = Zone.GetZoneId(value); - OnZoneChanged(); + currentZoneID.Value = Zone.GetZoneId(value); + //OnZoneChanged(); } } - private void OnZoneChanged() + private void OnZoneChanged(int previousValue, int newValue) { - Zone newZone = Zone.GetFromId(State.CurrentZone); - if(lastZone != newZone) + if(previousValue != newValue) { - if(lastZone != null) + Zone previousZone = Zone.GetFromId(previousValue); + Zone newZone = Zone.GetFromId(newValue); + if(previousZone != null) { - lastZone.OnFormationDeparting(this); + previousZone.OnFormationDeparting(this); } if(newZone != null) { newZone.OnFormationArrived(this); } - BoltLog.Info("Current zone changed from {0} to {1} on formation {2}", lastZone, newZone, this); - lastZone = newZone; + BoltLog.Info("Current zone changed from {0} to {1} on formation {2}", previousZone, newZone, this); } } public ZoneType ZoneType { - get => State.IsSpace ? ZoneType.Space : ZoneType.Ground; - set => State.IsSpace = value == ZoneType.Space; + get => isSpace.Value ? ZoneType.Space : ZoneType.Ground; + set => isSpace.Value = value == ZoneType.Space; } public Player Player { - get => BoltEntityCache.Get(State.Player); - set => State.Player = BoltEntityCache.Set(value); + get => player.Value.GetBehaviour(); + set => player.Value = value; } public string FormationName { - get => State.FormationName; + get => formationName.Value; set { - State.FormationName = value; + formationName.Value = value; keepFormationName = true; } } @@ -72,30 +111,29 @@ namespace GWConquest private Vector3 restingPosition; public float AnimCompletion { - get => animDistanceCovered / State.CurrentTransition.TransitionLength; + get => animDistanceCovered / CurrentTransition.TransitionLength; } public float AnimTimeLeft { - get => (State.CurrentTransition.TransitionLength - animDistanceCovered) / movementSpeed; + get => (CurrentTransition.TransitionLength - animDistanceCovered) / movementSpeed; } - public EntityList UnitEntities; - - public EntityList SubFormationEntities; + public IEnumerable Units { + get => units.Select(r => r.GetBehaviour()); + } - public IEnumerable Units - { - get => UnitEntities.Select((e,i) => e.Entity.GetComponent()); + public IEnumerable SubFormations { + get => units.Select(r => r.GetBehaviour()); } public int UnitCount { - get => UnitEntities.Count; + get => units.Count; } public Unit HeroUnit { - get => BoltEntityCache.Get(State.HeroUnit); - set => State.HeroUnit = BoltEntityCache.Set(value); + get => heroUnit.Value.GetBehaviour(); + set => heroUnit.Value = value; } public IEnumerable AllItems @@ -103,52 +141,54 @@ namespace GWConquest get => Units.SelectMany(u => u.Inventory); } - public IEnumerable SubFormations - { - get => SubFormationEntities.Select((e,i) => e.Entity.GetComponent()); + public IEnumerable PathQueue { + get => pathQueue.Select(id => Zone.GetFromId(id)); } - public ZoneList PathQueue; - public Formation MovementTargetFormation { - get => BoltEntityCache.Get(State.MovementTargetFormation); - set => State.MovementTargetFormation = BoltEntityCache.Set(value); + get => movementTargetFormation.Value.GetBehaviour(); + set => movementTargetFormation.Value = value; } public Formation MovementOriginFormation { - get => BoltEntityCache.Get(State.MovementOriginFormation); - set => State.MovementOriginFormation = BoltEntityCache.Set(value); + get => movementOriginFormation.Value.GetBehaviour(); + set => movementOriginFormation.Value = value; } public bool IsOnSpaceGroundTransition { - get => MovementState == FormationMovementState.Moving && Zone.GetFromId(State.CurrentTransition.OriginZone).zoneType != Zone.GetFromId(State.CurrentTransition.TargetZone).zoneType; + get => MovementState == FormationMovementState.Moving && CurrentTransition.OriginZone.zoneType != CurrentTransition.TargetZone.zoneType; + } + + public Transition CurrentTransition + { + get => currentTransition.Value; } public bool IsEmbarked { - get => State.IsEmbarked; - set => State.IsEmbarked = value; + get => isEmbarked.Value; + set => isEmbarked.Value = value; } public Formation ParentFormation { - get => BoltEntityCache.Get(State.ParentFormation); - set => State.ParentFormation = BoltEntityCache.Set(value); + get => parentFormation.Value.GetBehaviour(); + set => parentFormation.Value = value; } public const float MoraleCap = 27 * 3 * 100; public float Morale { - get => State.Morale; - set => State.Morale = value; + get => morale.Value; + set => morale.Value = value; } public float StartingMorale { - get => State.StartingMorale; - set => State.StartingMorale = value; + get => startingMorale.Value; + set => startingMorale.Value = value; } public FormationMovementState MovementState { - get => (FormationMovementState) State.MovementState; - set => State.MovementState = (int) value; + get => movementState.Value; + set => movementState.Value = value; } public bool IsMoving { @@ -158,17 +198,17 @@ namespace GWConquest } public float ActionCooldown { - get => State.ActionCooldown; + get => actionCooldown.Value; } public float ActionCooldownPercent { - get => State.ActionCooldown / State.ActionCooldownMax; + get =>actionCooldown.Value / actionCooldownMax.Value; } public void SetActionCooldown(float value) { - State.ActionCooldown = value; - State.ActionCooldownMax = value; + actionCooldown.Value = value; + actionCooldownMax.Value = value; } public GameObject movingArmyPrefab; @@ -180,8 +220,8 @@ namespace GWConquest public float movementSpeed {get => GameManager.Instance.DefaultMovementSpeed;} public int FormationNumber { - get => State.FormationNumber; - set => State.FormationNumber = value; + get => formationNumber.Value; + set => formationNumber.Value = value; } public bool keepFormationName = false; @@ -200,9 +240,9 @@ namespace GWConquest { return z.planet; } - else if(State.CurrentTransition.OriginZone != -1) + else if(CurrentTransition.OriginZoneID != -1) { - return Zone.GetFromId(State.CurrentTransition.OriginZone).planet; + return CurrentTransition.OriginZone.planet; } else return null; } @@ -237,7 +277,7 @@ namespace GWConquest public void StartMovingOnPath(List path, Formation targetFormation=null) { - if (path.Count > 1 && entity.IsControlled && CanMove) + if (path.Count > 1 && IsOwner && CanMove) { if(!CanMoveTo(path[0], path[1])) { @@ -282,14 +322,14 @@ namespace GWConquest if(IsEmbarked) { - if(ParentFormation != null && ParentFormation.entity.IsAttached) + if(ParentFormation != null) { MovementOriginFormation = ParentFormation; ParentFormation.RemoveSubFormation(this); } } - PathQueue.SetEntries(path); + pathQueue.SetEntries(path.Select(z => Zone.GetZoneId(z))); if(targetFormation != null) { @@ -307,7 +347,7 @@ namespace GWConquest public void MoveToZone(Zone target) { - if(entity.IsOwner && MovementState == FormationMovementState.PreparingMovement) + if(IsOwner && MovementState == FormationMovementState.PreparingMovement) { if(!CanMoveTo(currentZone, target)) { @@ -354,12 +394,12 @@ namespace GWConquest BoltLog.Warn($"There is {remainingFuel} required fuel remaining after consuming everything, this should not happen!"); } - State.CurrentTransition.OriginZone = Zone.GetZoneId(currentZone); - State.CurrentTransition.TargetZone = Zone.GetZoneId(target); - bool isGroundTransition = target.zoneType == ZoneType.Ground || currentZone.zoneType == ZoneType.Ground; - //float lengthFactor = isGroundTransition ? GameManager.Instance.GroundTransitionLengthFactor : GameManager.Instance.SpaceTransitionLengthFactor; - State.CurrentTransition.TransitionLength = Zone.GetTransitionLength(currentZone, target); - State.CurrentTransition.IsCurved = isGroundTransition; + currentTransition.Value = new Transition() { + OriginZoneID = Zone.GetZoneId(currentZone), + TargetZoneID = Zone.GetZoneId(target), + TransitionLength = Zone.GetTransitionLength(currentZone, target), + IsCurved = target.zoneType == ZoneType.Ground || currentZone.zoneType == ZoneType.Ground + }; CoveredDistance = 0; currentZone = null; @@ -386,15 +426,15 @@ namespace GWConquest } - private void OnTransitStateChanged() + private void OnTransitStateChanged(FormationMovementState previousValue, FormationMovementState newValue) { animDistanceCovered = 0; - if(MovementState == FormationMovementState.Moving) + if(newValue == FormationMovementState.Moving) { - var originZone = Zone.GetFromId(State.CurrentTransition.OriginZone); - var targetZone = Zone.GetFromId(State.CurrentTransition.TargetZone); + var originZone = CurrentTransition.OriginZone; + var targetZone = CurrentTransition.TargetZone; - if (State.CurrentTransition.IsCurved) + if (CurrentTransition.IsCurved) { if (IsOnSpaceGroundTransition && IngameUI.PlanetViewEnabled) { @@ -423,11 +463,13 @@ namespace GWConquest } } - public override void SimulateOwner() + public void FixedUpdate() { + if(IsServer) + { if(IsEmbarked) { - if(ParentFormation != null && ParentFormation.entity.IsAttached) + if(ParentFormation != null) { if(currentZone != ParentFormation.currentZone) { @@ -443,19 +485,22 @@ namespace GWConquest if(MovementState == FormationMovementState.Moving) { CoveredDistance += movementSpeed * BoltNetwork.FrameDeltaTime; - if (CoveredDistance >= State.CurrentTransition.TransitionLength) + if (CoveredDistance >= CurrentTransition.TransitionLength) { - currentZone = Zone.GetFromId(State.CurrentTransition.TargetZone); + currentZone = CurrentTransition.TargetZone; if(currentZone.zoneType == ZoneType.Space) { - State.FleetRestingPoint = currentZone.planet.GetFleetArrivalPosition(Zone.GetFromId(State.CurrentTransition.OriginZone)); + fleetRestingPoint.Value = currentZone.planet.GetFleetArrivalPosition(CurrentTransition.OriginZone); } - State.CurrentTransition.OriginZone = -1; - State.CurrentTransition.TargetZone = -1; - State.CurrentTransition.TransitionLength = 0; + currentTransition.Value = new Transition() + { + OriginZoneID = -1, + TargetZoneID = -1, + TransitionLength = 0 + }; CoveredDistance = 0; MovementState = FormationMovementState.FinishingMovement; @@ -466,27 +511,27 @@ namespace GWConquest if(ActionCooldown <= 0) { - State.ActionCooldown = 0; + actionCooldown.Value = 0; if(MovementState == FormationMovementState.PreparingMovement) { Zone z = PathQueue.First(); - PathQueue.RemoveAt(0); + pathQueue.RemoveAt(0); MoveToZone(z); } else if(MovementState == FormationMovementState.FinishingMovement) { - if(PathQueue.Count > 0) + if(pathQueue.Count > 0) { - if(CanMoveTo(currentZone, PathQueue[0])) + if(CanMoveTo(currentZone, PathQueue.First())) { SetActionCooldown(GameManager.Instance.MovementStartingCooldown); MovementState = FormationMovementState.PreparingMovement; } else { - BoltLog.Info($"Formation {this} has not enough fuel for transition {currentZone} -> {PathQueue[0]}"); - PathQueue.Clear(); + BoltLog.Info($"Formation {this} has not enough fuel for transition {currentZone} -> {PathQueue.First()}"); + pathQueue.Clear(); MovementState = FormationMovementState.Idle; } @@ -495,7 +540,7 @@ namespace GWConquest { if(MovementTargetFormation != null) { - if(MovementTargetFormation.entity.IsAttached && MovementTargetFormation.currentZone == currentZone) + if(MovementTargetFormation.HasNetworkObject && MovementTargetFormation.currentZone == currentZone) { MovementTargetFormation.AddSubFormation(this); } @@ -511,7 +556,8 @@ namespace GWConquest } } else { - State.ActionCooldown -= BoltNetwork.FrameDeltaTime; + actionCooldown.Value -= Time.fixedDeltaTime; + } } } @@ -536,7 +582,7 @@ namespace GWConquest private void Update() { - if (entity.IsAttached) + if (HasNetworkObject) { if (GameManager.EntitiesLoaded) { @@ -556,13 +602,13 @@ namespace GWConquest if (MovementState == FormationMovementState.Moving) { animDistanceCovered += movementSpeed * Time.deltaTime; - Zone originZone = Zone.GetFromId(State.CurrentTransition.OriginZone); - Zone targetZone = Zone.GetFromId(State.CurrentTransition.TargetZone); + Zone originZone = CurrentTransition.OriginZone; + Zone targetZone = CurrentTransition.TargetZone; Vector3 originPos = originZone.transform.position; Vector3 targetPos = targetZone.transform.position; - if (!State.CurrentTransition.IsCurved) + if (!CurrentTransition.IsCurved) { originPos += originZone.planet.GetFleetArrivalPosition(targetZone) * originZone.planet.GetCurrentCircleRadius(); targetPos += targetZone.planet.GetFleetArrivalPosition(originZone) * targetZone.planet.GetCurrentCircleRadius(); @@ -588,7 +634,7 @@ namespace GWConquest //{ newPos = Vector3.Lerp(originPos, targetPos, - animDistanceCovered / State.CurrentTransition.TransitionLength); + animDistanceCovered / CurrentTransition.TransitionLength); newRot = Quaternion.LookRotation(newPos - oldPos, Vector3.up); //} @@ -684,43 +730,41 @@ namespace GWConquest var planet = currentZone.planet; float currentCircleRadius = planet.GetCurrentCircleRadius(); - return planet.transform.position + State.FleetRestingPoint * 0.5f * currentCircleRadius; + return planet.transform.position + fleetRestingPoint.Value * 0.5f * currentCircleRadius; } - public override void Attached() + public override void OnNetworkSpawn() { BoltLog.Info("Formation attached: {0}", this); - UnitEntities = new EntityList(State, "Units"); - SubFormationEntities = new EntityList(State, "SubFormations"); FormationInventory = new CombinedInventory(() => Units.Select(u => u.Inventory)); - PathQueue = new ZoneList(State, "PathQueue"); - AllFormations.Add(this); - if(entity.IsOwner) + if(IsOwner) { - State.CurrentZone = -1; + currentZoneID.Value = -1; MovementState = FormationMovementState.Idle; CoveredDistance = 0; FormationNumber = UnityEngine.Random.Range(1, 100); } - State.AddCallback("MovementState", OnTransitStateChanged); - State.AddCallback("CurrentZone", OnZoneChanged); + movementState.OnValueChanged += OnTransitStateChanged; + currentZoneID.OnValueChanged += OnZoneChanged; + + base.OnNetworkSpawn(); } public float GetFormationStrength() { - if(entity.IsAttached) + if(HasNetworkObject) { - return UnitEntities.Sum(unit => { - if(unit.Entity != null && unit.Entity.IsAttached) + return Units.Sum(unit => { + if(unit != null && unit.HasNetworkObject) { - return unit.Entity.GetComponent().Class.UnitStrength; + return unit.Class.UnitStrength; } else { return 0f; @@ -740,7 +784,7 @@ namespace GWConquest { currentZone.OnFormationChanged(this); } - if(entity.IsOwner) + if(IsOwner) { if(!keepFormationName) { @@ -754,7 +798,7 @@ namespace GWConquest public void OnUnitRemoved(Unit unit) { OnUnitsChanged?.Invoke(); - if(entity.IsOwner) + if(IsOwner) { if(!keepFormationName) { @@ -765,14 +809,14 @@ namespace GWConquest public void AddSubFormation(Formation f) { - SubFormationEntities.Add(f.entity); + subFormations.Add(f); f.ParentFormation = this; f.IsEmbarked = true; } public void RemoveSubFormation(Formation f) { - SubFormationEntities.Remove(f.entity); + subFormations.Remove(f); f.ParentFormation = null; f.IsEmbarked = false; } @@ -893,7 +937,7 @@ namespace GWConquest } } - State.FormationName = name; + formationName.Value = name; } public override string ToString() @@ -957,7 +1001,7 @@ namespace GWConquest foreach(Unit u in Units) { u.BattleState = isArriving ? BattleUnitState.Arriving : BattleUnitState.InReserve; - u.state.ActionCooldown = 0; + u.ResetActionCooldown(); if(!battle.IsSpaceBattle && !u.Class.IsHero) { @@ -988,12 +1032,12 @@ namespace GWConquest public void RemoveDeadUnits() { - for(int i = UnitEntities.Count-1; i >= 0; i--) + for(int i = units.Count-1; i >= 0; i--) { - var unit = UnitEntities[i].Entity.GetComponent(); + var unit = units[i].GetBehaviour(); if(unit.IsDead) { - UnitEntities.RemoveAt(i); + units.RemoveAt(i); OnUnitRemoved(unit); BoltNetwork.Destroy(unit.gameObject); @@ -1002,7 +1046,33 @@ namespace GWConquest } } + public void AddUnit(Unit unit) + { + units.Add(unit); + } + + public static Formation SpawnFormation(Zone zone, Player player) + { + BoltEntity formationEntity = BoltNetwork.Instantiate(BoltPrefabs.Formation); + player.AssignControl(formationEntity); + Formation playerFormation = formationEntity.GetComponent(); + playerFormation.Initialize(zone, player); + return playerFormation; + } + + public void Initialize(Zone zone, Player player) + { + gameObject.name = "Formation_" + NetworkObjectId.ToString(); + Player = player; + currentZone = zone; + ZoneType = zone.zoneType; + if(ZoneType == ZoneType.Space) + { + Vector3 startPos = zone.planet.GetFleetRestingPosition(); + fleetRestingPoint.Value = startPos; + } + } } diff --git a/Assets/GWConquest/Scripts/GWNetworkArray.cs b/Assets/GWConquest/Scripts/GWNetworkArray.cs new file mode 100644 index 0000000..ecf72d6 --- /dev/null +++ b/Assets/GWConquest/Scripts/GWNetworkArray.cs @@ -0,0 +1,5 @@ +using Unity.Netcode; + +namespace GWConquest { + +} \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/GWNetworkArray.cs.meta b/Assets/GWConquest/Scripts/GWNetworkArray.cs.meta new file mode 100644 index 0000000..9809ab7 --- /dev/null +++ b/Assets/GWConquest/Scripts/GWNetworkArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5893ec9dc0d5802479ca61e4b13ac64f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GWConquest/Scripts/GWNetworkList.cs b/Assets/GWConquest/Scripts/GWNetworkList.cs new file mode 100644 index 0000000..b3e413c --- /dev/null +++ b/Assets/GWConquest/Scripts/GWNetworkList.cs @@ -0,0 +1,37 @@ +using Unity.Netcode; +using System.Collections; +using System.Collections.Generic; +using System; + +namespace GWConquest +{ + public class GWNetworkList : NetworkList, IList where T : unmanaged, IEquatable + { + public void CopyTo(T[] array, int arrayIndex) + { + for(int i = 0; i < Count; i++) + { + array[i+arrayIndex] = this[i]; + } + } + + public bool IsReadOnly { + get => false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void SetEntries(IEnumerable values) + { + Clear(); + foreach(T val in values) + { + Add(val); + } + } + + } +} \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/GWNetworkList.cs.meta b/Assets/GWConquest/Scripts/GWNetworkList.cs.meta new file mode 100644 index 0000000..c99657b --- /dev/null +++ b/Assets/GWConquest/Scripts/GWNetworkList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4288849507c89dc46a142bbce180f2a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GWConquest/Scripts/GWNullable.cs b/Assets/GWConquest/Scripts/GWNullable.cs new file mode 100644 index 0000000..482aece --- /dev/null +++ b/Assets/GWConquest/Scripts/GWNullable.cs @@ -0,0 +1,74 @@ +using UnityEngine; +using Unity.Netcode; +using System; + +namespace GWConquest { + public struct NullableNetworkBehaviourReference : INetworkSerializable, IEquatable + { + private NetworkBehaviourReference reference; + private bool hasValue; + + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter + { + serializer.SerializeValue(ref hasValue); + if(hasValue) + { + serializer.SerializeValue(ref reference); + } + } + + public bool HasValue => hasValue; + + public NetworkBehaviour Behaviour + { + get { + if(hasValue) + { + if(reference.TryGet(out NetworkBehaviour beh)) + { + return beh; + } + else { + Debug.LogWarning($"Could not get network behaviour reference {reference}"); + return null; + } + } + else return null; + } + } + + public T GetBehaviour() where T : NetworkBehaviour + { + return Behaviour as T; + } + + public static implicit operator NullableNetworkBehaviourReference(NetworkBehaviour beh) + { + if(beh != null) + { + return new NullableNetworkBehaviourReference() { hasValue = true, reference = beh }; + } + else { + return new NullableNetworkBehaviourReference() { hasValue = false }; + } + } + + public static implicit operator NetworkBehaviour(NullableNetworkBehaviourReference reference) + => reference.Behaviour; + + public bool Equals(NullableNetworkBehaviourReference other) + { + if(hasValue != other.hasValue) + return false; + if(hasValue) + { + return reference.Equals(other.reference); + } + else + { + return true; + } + } + + } +} \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/GWNullable.cs.meta b/Assets/GWConquest/Scripts/GWNullable.cs.meta new file mode 100644 index 0000000..70791fe --- /dev/null +++ b/Assets/GWConquest/Scripts/GWNullable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1420b6fa4f0378740b9eea39c2f80233 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GWConquest/Scripts/Inventory.cs b/Assets/GWConquest/Scripts/Inventory.cs index b9df833..2fe9ebb 100644 --- a/Assets/GWConquest/Scripts/Inventory.cs +++ b/Assets/GWConquest/Scripts/Inventory.cs @@ -5,23 +5,24 @@ using System; using System.Linq; using System.Collections.Generic; using UnityEngine; +using Unity.Netcode; namespace GWConquest { [System.Serializable] - public struct ItemStack : IIconObject, IProtocolToken + public struct ItemStack : IIconObject, INetworkSerializable, IEquatable { - public static int GetMaxStackSize(string item) { + public static int GetMaxStackSize(ushort item) { return GameManager.Instance.MaxStackSize; } public bool IsUnit; - public string ItemName; - public NetworkId UnitId; + public ushort ItemName; + public NetworkBehaviourReference UnitReference; public Unit Unit { - get => BoltNetwork.FindEntity(UnitId)?.GetComponent(); + get => UnitReference.GetBehaviour(); } public bool Stackable; public int Amount; @@ -71,7 +72,23 @@ namespace GWConquest } return new ItemStack { IsUnit = false, - ItemName = itemName, + ItemName = ItemRegistry.Instance.IDFromName(itemName), + Stackable = item.Stackable, + Amount = amount + }; + } + + public static ItemStack FromItem(ushort itemID, int amount=1) + { + var item = ItemRegistry.Instance.GetEntry(itemID); + if(!item.Stackable && amount > 1) + { + BoltLog.Warn("Item {0} is not stackable to amount {1}", itemID, amount); + amount = 1; + } + return new ItemStack { + IsUnit = false, + ItemName = itemID, Stackable = item.Stackable, Amount = amount }; @@ -81,7 +98,7 @@ namespace GWConquest { return new ItemStack { IsUnit = true, - UnitId = unit.entity.NetworkId, + UnitReference = unit, Stackable = false, Amount = amount }; @@ -90,97 +107,88 @@ namespace GWConquest public ItemStack Copy() { return new ItemStack() { IsUnit = IsUnit, - UnitId = UnitId, + UnitReference = UnitReference, ItemName = ItemName, Stackable = Stackable, Amount = Amount }; } - public static bool ItemsEqual(ItemStack stack1, ItemStack stack2) + public bool Equals(ItemStack other) { - return stack1.IsUnit == stack2.IsUnit - && (stack1.IsUnit ? - (stack1.Unit == stack2.Unit) : - (stack1.ItemName == stack2.ItemName)); + return IsUnit == other.IsUnit + && (IsUnit ? + (Unit == other.Unit) : + (ItemName.Equals(other.ItemName))); } - public void Write(UdpPacket packet) + public static bool ItemsEqual(ItemStack stack1, ItemStack stack2) { - packet.WriteBool(IsUnit); - if(IsUnit) - { - packet.WriteULong(UnitId.PackedValue); - } - else { - packet.WriteString(ItemName); - } - - packet.WriteBool(Stackable); - packet.WriteInt(Amount); + return stack1.Equals(stack2); } - public void Read(UdpPacket packet) + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { - IsUnit = packet.ReadBool(); - if(IsUnit) + if (serializer.IsReader) { - UnitId = new NetworkId(packet.ReadULong()); + var reader = serializer.GetFastBufferReader(); + reader.ReadValueSafe(out IsUnit); + if(IsUnit) + { + serializer.SerializeValue(ref UnitReference); + } + else { + reader.ReadValueSafe(out ItemName); + } + reader.ReadValueSafe(out Stackable); + reader.ReadValueSafe(out Amount); + } + else + { + var writer = serializer.GetFastBufferWriter(); + writer.WriteValueSafe(IsUnit); + if(IsUnit) + { + serializer.SerializeValue(ref UnitReference); + } + else { + writer.WriteValueSafe(ItemName); + } + writer.WriteValueSafe(Stackable); + writer.WriteValueSafe(Amount); } - else { - ItemName = packet.ReadString(); - } - Stackable = packet.ReadBool(); - Amount = packet.ReadInt(); - } - - } - - public class InventoryToken : BoltListToken - { - public override void WriteEntry(UdpPacket packet, ItemStack item) - { - item.Write(packet); } - public override ItemStack ReadEntry(UdpPacket packet) - { - var item = new ItemStack(); - item.Read(packet); - return item; - } } public interface IInventory { - int GetItemAmount(string item); + int GetItemAmount(ushort item); - bool AddItem(string item, int amount); + bool AddItem(ushort item, int amount); - int GetRemainingSpace(string item); + int GetRemainingSpace(ushort item); - bool RemoveItem(string item, int amount); + bool RemoveItem(ushort item, int amount); } - public class Inventory : BoltList, IInventory + public class Inventory : GWNetworkList, IInventory { // Question: Can there be more then one half-full stack for an item type?? - public Inventory(IState _state, string _propertyName) : base(_state, _propertyName) { } - public int StorageCapacity = int.MaxValue; - public int GetItemAmount(string item) + public int GetItemAmount(ushort item) { return this.Where(stack => (!stack.IsUnit && stack.ItemName == item)).Sum(stack => stack.Amount); } - public int GetItemStackAmount(string item) + public int GetItemStackAmount(ushort item) { return this.Count(stack => !stack.IsUnit && stack.ItemName == item); } - public bool AddItem(string item, int amount) + public bool AddItem(ushort item, int amount) { if(GetRemainingSpace(item) >= amount) { int maxStackSize = ItemStack.GetMaxStackSize(item); @@ -219,7 +227,7 @@ namespace GWConquest } } - public int GetRemainingSpace(string item) + public int GetRemainingSpace(ushort item) { int maxStackSize = ItemStack.GetMaxStackSize(item); int emptyStacks = StorageCapacity - Count; @@ -231,7 +239,7 @@ namespace GWConquest return emptyStacks * maxStackSize + restAmount; } - public bool RemoveItem(string item, int amount) + public bool RemoveItem(ushort item, int amount) { int currentAmount = GetItemAmount(item); @@ -258,7 +266,7 @@ namespace GWConquest } } - private int RemoveFromStacks(string item, int amount, bool partialStacksOnly) + private int RemoveFromStacks(ushort item, int amount, bool partialStacksOnly) { int maxStackSize = ItemStack.GetMaxStackSize(item); int amountToRemove = amount; @@ -291,7 +299,7 @@ namespace GWConquest { //TODO Unit Items - Dictionary dict = new Dictionary(); + Dictionary dict = new Dictionary(); foreach(var stack in items) { @@ -323,18 +331,18 @@ namespace GWConquest InventoryFunc = _inventoryFunc; } - public int GetItemAmount(string item) + public int GetItemAmount(ushort item) { return InventoryFunc().Sum(inv => inv.GetItemAmount(item)); } - public int GetRemainingSpace(string item) + public int GetRemainingSpace(ushort item) { return InventoryFunc().Sum(inv => inv.GetRemainingSpace(item)); } // TODO organize items for maximum space - public bool AddItem(string item, int amount) + public bool AddItem(ushort item, int amount) { if(GetRemainingSpace(item) >= amount) { @@ -362,7 +370,7 @@ namespace GWConquest } } - public bool RemoveItem(string item, int amount) + public bool RemoveItem(ushort item, int amount) { if(GetItemAmount(item) >= amount) { @@ -394,12 +402,12 @@ namespace GWConquest public static class InventoryExtensions { - public static bool HasItem(this IInventory inventory, string item, int amount) + public static bool HasItem(this IInventory inventory, ushort item, int amount) { return inventory.GetItemAmount(item) >= amount; } - public static bool DoesItemFit(this IInventory inventory, string item, int amount) + public static bool DoesItemFit(this IInventory inventory, ushort item, int amount) { return inventory.GetRemainingSpace(item) >= amount; } diff --git a/Assets/GWConquest/Scripts/ItemRegistry.cs b/Assets/GWConquest/Scripts/ItemRegistry.cs index 2e1eac9..d4c033c 100644 --- a/Assets/GWConquest/Scripts/ItemRegistry.cs +++ b/Assets/GWConquest/Scripts/ItemRegistry.cs @@ -1,10 +1,17 @@ using UnityEngine; using System.Collections.Generic; +using Unity.Netcode; +using System; namespace GWConquest { public class ItemRegistry : MonoBehaviour { + public static ushort kFuel; + public static ushort kSupplies; + public static ushort kFood; + public static ushort kRecruits; + private static ItemRegistry _instance; public static ItemRegistry Instance { get { @@ -38,6 +45,11 @@ namespace GWConquest { itemMap.Add(e.Name, e); } + + kFuel = IDFromName("Fuel"); + kSupplies = IDFromName("Supplies"); + kFood = IDFromName("Food"); + kRecruits = IDFromName("Recruits"); } public ItemEntry GetEntry(string itemName) @@ -47,6 +59,31 @@ namespace GWConquest } else return null; } + + public ItemEntry GetEntry(ushort id) + { + if(id >= Entries.Length) + { + Debug.LogWarning($"Unknown item id: {id}"); + return null; + } + else { + return Entries[id]; + } + } + + public ushort IDFromName(string ItemName) + { + for(ushort i = 0; i < Entries.Length; i++) + { + if(Entries[i].Name == ItemName) + { + return i; + } + } + Debug.LogError($"Unknown item name: {ItemName}"); + return 0; + } } } diff --git a/Assets/GWConquest/Scripts/Planet.cs b/Assets/GWConquest/Scripts/Planet.cs index ba948d7..67fbb47 100644 --- a/Assets/GWConquest/Scripts/Planet.cs +++ b/Assets/GWConquest/Scripts/Planet.cs @@ -4,11 +4,13 @@ using System.Linq; using UnityEngine; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; +using Unity.Collections; namespace GWConquest { [ExecuteInEditMode] - public class Planet : GWBoltBehaviour + public class Planet : NetworkBehaviour { private static Planet[] _allPlanets; public static Planet[] AllPlanets @@ -32,6 +34,14 @@ namespace GWConquest _allPlanets = null; } + private NetworkVariable planetNameVar; + private NetworkVariable spaceZoneID; + private NetworkVariable attackZoneID; + private GWNetworkList connectedPlanets; + private NetworkVariable planetPrefab; + private NetworkVariable prefabRotation; + private NetworkVariable prefabScale; + public GameObject textPrefab; public bool PlanetStatic = false; @@ -43,7 +53,7 @@ namespace GWConquest public string planetName; public string PlanetName { - get => State.PlanetName; + get => planetNameVar.ToString(); } public string BackgroundMusic; @@ -103,8 +113,6 @@ namespace GWConquest [HideInInspector] public List connections = new List(); - public EntityList ConnectedPlanetsList; - public PathfindingGraph pathfindingGraph; public IInventory PlanetInventory; @@ -136,9 +144,12 @@ namespace GWConquest return connections.Exists(conn => (conn.planet1 == planet || conn.planet2 == planet)); } - public override void Attached() + public IEnumerable ConnectedPlanets { + get => connectedPlanets.Select(r => r.GetBehaviour()); + } + + public override void OnNetworkSpawn() { - ConnectedPlanetsList = new EntityList(State, "ConnectedPlanets"); /*if(BoltNetwork.IsClient) { @@ -179,11 +190,11 @@ namespace GWConquest zone.zoneType = ZoneType.Space; } - if(entity.IsOwner) + if(IsOwner) { - State.SpaceZoneID = Zone.GetZoneId(GetMainZone(ZoneType.Space)); - State.AttackZoneID = Zone.GetZoneId(GetAttackZones(ZoneType.Ground)[0]); - State.PlanetName = planetName; + spaceZoneID.Value = Zone.GetZoneId(GetMainZone(ZoneType.Space)); + attackZoneID.Value = Zone.GetZoneId(GetAttackZones(ZoneType.Ground)[0]); + planetNameVar.Value = planetName; } PlanetPlacement.Instance.SpawnAsteroids(this); @@ -205,15 +216,15 @@ namespace GWConquest zone.zoneType = ZoneType.Space; } - if(entity.IsOwner) + if(IsOwner) { - State.SpaceZoneID = Zone.GetZoneId(GetMainZone(ZoneType.Space)); - State.AttackZoneID = Zone.GetZoneId(GetAttackZones(ZoneType.Ground)[0]); + spaceZoneID.Value = Zone.GetZoneId(GetMainZone(ZoneType.Space)); + attackZoneID.Value = Zone.GetZoneId(GetAttackZones(ZoneType.Ground)[0]); } else { - planetName = State.PlanetName; - GetMainZone(ZoneType.Space).SetZoneId(State.SpaceZoneID); - GetAttackZones(ZoneType.Ground)[0].SetZoneId(State.AttackZoneID); + planetName = PlanetName; + GetMainZone(ZoneType.Space).SetZoneId(spaceZoneID.Value); + GetAttackZones(ZoneType.Ground)[0].SetZoneId(attackZoneID.Value); PlanetPlacement.Instance.InitPlanetPrefab(this); } @@ -225,11 +236,11 @@ namespace GWConquest { if (conn.planet1 == this) { - ConnectedPlanetsList.Add(conn.planet2.entity); + connectedPlanets.Add(conn.planet2); } else if (conn.planet2 == this) { - ConnectedPlanetsList.Add(conn.planet1.entity); + connectedPlanets.Add(conn.planet1); } } } @@ -238,9 +249,9 @@ namespace GWConquest { var pp = PlanetPlacement.Instance; - foreach(var entity in ConnectedPlanetsList) + foreach(var beh in connectedPlanets) { - var otherPlanet = entity.Entity.GetComponent(); + var otherPlanet = beh.GetBehaviour(); if(!IsConnectedTo(otherPlanet)) { pp.SpawnPlanetConnection(this, otherPlanet); @@ -250,7 +261,7 @@ namespace GWConquest public void FinishSetup() { - if(entity.IsOwner) + if(IsOwner) { UpdateConnectionsServer(); } @@ -327,9 +338,9 @@ namespace GWConquest var currentPlayers = GetAllFormations(ZoneType.Space).Select(f => f.Player).Distinct(); foreach(Player player in currentPlayers) { - if(!player.Faction.IsAI && GetVisibilityStatus(player) != PlanetVisibilityStatus.Visible) + if(!player.Faction.IsAI && player.GetPlanetVisibility(this) != PlanetVisibilityStatus.Visible) { - SetPlanetVisible(player); + player.SetPlanetVisible(this); } } } @@ -356,7 +367,7 @@ namespace GWConquest if(mainZone != null) { var district = mainZone.GetComponent(); - if(district != null && district.entity.IsAttached) + if(district != null && district.IsSpawned) { return district.ControllingPlayer; } @@ -381,26 +392,12 @@ namespace GWConquest } } - public PlanetVisibilityStatus GetVisibilityStatus(Player player) - { - if(player.VisiblePlanetsList.FirstOrDefault(e => e.Entity == entity) != null) - { - return PlanetVisibilityStatus.Visible; - } - else if(player.KnownPlanetsList.FirstOrDefault(e => e.Entity == entity) != null) - { - return PlanetVisibilityStatus.Known; - } - else { - return PlanetVisibilityStatus.Hidden; - } - } public static void UpdateVisibilityStatus() { foreach(var planet in Planet.AllPlanets) { - var status = planet.GetVisibilityStatus(Player.CurrentPlayer); + var status = Player.CurrentPlayer.GetPlanetVisibility(planet); if(status != planet.VisibilityStatus) { planet.VisibilityStatus = status; @@ -433,22 +430,7 @@ namespace GWConquest } } - public void SetPlanetVisible(Player player) - { - if(GetVisibilityStatus(player) != PlanetVisibilityStatus.Visible) - { - player.VisiblePlanetsList.Add(entity); - - foreach(var cpe in ConnectedPlanetsList) - { - var otherPlanet = cpe.Entity.GetComponent(); - if(otherPlanet.GetVisibilityStatus(player) == PlanetVisibilityStatus.Hidden) - { - player.KnownPlanetsList.Add(otherPlanet.entity); - } - } - } - } + public void UpdatePlanetRenderer() { diff --git a/Assets/GWConquest/Scripts/Player.cs b/Assets/GWConquest/Scripts/Player.cs index e309f68..bd3a4c3 100644 --- a/Assets/GWConquest/Scripts/Player.cs +++ b/Assets/GWConquest/Scripts/Player.cs @@ -3,11 +3,21 @@ using System.Collections.Generic; using System.Linq; using Photon.Bolt; using Photon.Bolt.Utils; +using Unity.Netcode; +using Unity.Collections; namespace GWConquest { - public class Player : GWBoltBehaviour + public class Player : NetworkBehaviour { + private NetworkVariable playerID; + private NetworkVariable playerName; + private NetworkVariable factionIndex; + private NetworkVariable isAI; + private NetworkVariable credits; + private GWNetworkList knownPlanetsList; + private GWNetworkList visiblePlanetsList; + public static List PlayerList = new List(); public static Player CurrentPlayer; @@ -19,7 +29,11 @@ namespace GWConquest public Faction Faction { - get => GameManager.Instance.Factions[State.FactionIndex]; + get => GameManager.Instance.Factions[factionIndex.Value]; + } + + public bool IsAI { + get => isAI.Value; } public Color Color @@ -29,83 +43,71 @@ namespace GWConquest public int Credits { - get => State.Credits; - set => State.Credits = value; + get => credits.Value; + set => credits.Value = value; } - public int PlayerId + public ushort PlayerId { - get => State.PlayerId; + get => playerID.Value; } - public EntityList KnownPlanetsList; - public EntityList VisiblePlanetsList; + private void OnPlanetsUpdated(NetworkListEvent ev) + { + if(GameManager.EntitiesLoaded) + { + Planet.UpdateVisibilityStatus(); + } + } - public override void Attached() + public override void OnNetworkSpawn() { Debug.Log("Attaching player..."); PlayerList.Add(this); - if(entity.IsOwner) + if(IsOwner) { - State.PlayerId = PlayerList.IndexOf(this); + playerID.Value = (ushort) PlayerList.IndexOf(this); } - KnownPlanetsList = new EntityList(State, "KnownPlanetsList"); - VisiblePlanetsList = new EntityList(State, "VisiblePlanetsList"); - - State.AddCallback("KnownPlanetsList", () => { - if(GameManager.EntitiesLoaded) - { - Planet.UpdateVisibilityStatus(); - } - }); - - State.AddCallback("VisiblePlanetsList", () => { - if(GameManager.EntitiesLoaded) - { - Planet.UpdateVisibilityStatus(); - } - }); + knownPlanetsList.OnListChanged += OnPlanetsUpdated; + visiblePlanetsList.OnListChanged += OnPlanetsUpdated; } - public override void Detached() + public override void OnNetworkDespawn() { PlayerList.Remove(this); } - public override void ControlGained() + public override void OnGainedOwnership() { CurrentPlayer = this; - BoltLog.Info("Current player is {0} with connection ID {1}", this, State.ConnectionId); + BoltLog.Info("Current player is {0} with connection ID {1}", this, OwnerClientId); } - public BoltConnection Connection + public NetworkClient Client { get { - if(State.IsAI || State.IsHost) + if(IsAI) { return null; } else { - return BoltNetwork.Connections.FirstOrDefault(conn => conn.ConnectionId == State.ConnectionId); + return NetworkManager.ConnectedClients[OwnerClientId]; } } } - public void AssignControl(BoltEntity entity) + public void AssignControl(NetworkObject obj) { - if(State.IsHost) + if(!IsAI) { - entity.TakeControl(); - } - else if(!State.IsAI) - { - entity.AssignControl(Connection); + obj.ChangeOwnership(OwnerClientId); } + } public void AssignStartingPlanets() @@ -119,14 +121,45 @@ namespace GWConquest d.ControllingPlayer = this; } - p.SetPlanetVisible(this); + SetPlanetVisible(p); } } } public override string ToString() { - return string.Format("Player (ID {0}, Faction {1})", State.PlayerId, Faction.FactionName); + return string.Format("Player (ID {0}, Faction {1})", PlayerId, Faction.FactionName); + } + + public PlanetVisibilityStatus GetPlanetVisibility(Planet planet) + { + if(visiblePlanetsList.Contains(planet)) + { + return PlanetVisibilityStatus.Visible; + } + else if(knownPlanetsList.Contains(planet)) + { + return PlanetVisibilityStatus.Known; + } + else { + return PlanetVisibilityStatus.Hidden; + } + } + + public void SetPlanetVisible(Planet planet) + { + if(GetPlanetVisibility(planet) != PlanetVisibilityStatus.Visible) + { + visiblePlanetsList.Add(planet); + + foreach(var otherPlanet in planet.ConnectedPlanets) + { + if(GetPlanetVisibility(otherPlanet) == PlanetVisibilityStatus.Hidden) + { + knownPlanetsList.Add(otherPlanet); + } + } + } } } diff --git a/Assets/GWConquest/Scripts/UID.cs b/Assets/GWConquest/Scripts/UID.cs new file mode 100644 index 0000000..f9c5f77 --- /dev/null +++ b/Assets/GWConquest/Scripts/UID.cs @@ -0,0 +1,44 @@ +using Unity.Netcode; +using System; + +namespace GWConquest { + public struct UID : INetworkSerializeByMemcpy, IEquatable { + public ushort value; + + public UID(ushort _value) + { + value = _value; + } + + public bool Equals(UID other) + { + return value == other.value; + } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + return Equals((UID)obj); + } + + public override int GetHashCode() + { + return value; + } + + public static bool operator ==(UID id1, UID id2) + { + return id1.value == id2.value; + } + + public static bool operator !=(UID id1, UID id2) + { + return id1.value != id2.value; + } + + } +} \ No newline at end of file diff --git a/Assets/GWConquest/Scripts/UID.cs.meta b/Assets/GWConquest/Scripts/UID.cs.meta new file mode 100644 index 0000000..44aaf66 --- /dev/null +++ b/Assets/GWConquest/Scripts/UID.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7793d4e07f641c04981bb5c3c3c33c90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GWConquest/Scripts/Unit.cs b/Assets/GWConquest/Scripts/Unit.cs index 86363d4..f5e30de 100644 --- a/Assets/GWConquest/Scripts/Unit.cs +++ b/Assets/GWConquest/Scripts/Unit.cs @@ -2,7 +2,7 @@ using UnityEngine; using Photon.Bolt; using Photon.Bolt.Utils; -using System.Collections.Generic; +using Unity.Netcode; namespace GWConquest { @@ -10,19 +10,38 @@ namespace GWConquest Sprite Icon {get;} } - public class Unit : GWBoltEntityListener, IIconObject + public class Unit : NetworkBehaviour, IIconObject { + private NetworkVariable unitClass; + private NetworkVariable formation; + private NetworkVariable hitpoints; + private NetworkVariable shields; + private NetworkVariable shieldsCooldown; + private NetworkVariable revealState; + private NetworkVariable battleState; + private NetworkVariable actionCooldown; + private NetworkVariable actionCooldownMax; + private Inventory inventory; + private Inventory equipment; + private NetworkVariable flankTarget; + private NetworkVariable morale; + private NetworkVariable supplies; + private NetworkVariable fuel; + private NetworkVariable food; + + public UnitClass Class { - get => UnitClass.FromName(State.UnitClass); + get => UnitClass.FromID(unitClass.Value); set { if(value == null) { - State.UnitClass = null; + Debug.LogError($"Unit Class set to null on unit {this}"); + unitClass.Value = 0; } else { - State.UnitClass = value.ShortName; + unitClass.Value = value.ID; Inventory.StorageCapacity = value.InventorySlots; Equipment.StorageCapacity = value.EquipmentSlots; @@ -37,24 +56,24 @@ namespace GWConquest public Formation Formation { - get => BoltEntityCache.Get(State.Formation); - set => State.Formation = BoltEntityCache.Set(value); + get => formation.Value.GetBehaviour(); + set => formation.Value = value; } public int Hitpoints { - get => State.Hitpoints; - set => State.Hitpoints = value; + get => hitpoints.Value; + set => hitpoints.Value = value; } public float Shields { - get => State.Shields; - set => State.Shields = value; + get => shields.Value; + set => shields.Value = value; } public float ShieldsCooldown { - get => State.ShieldsCooldown; - set => State.ShieldsCooldown = value; + get => shieldsCooldown.Value; + set => shieldsCooldown.Value = value; } public float Armour { @@ -75,23 +94,23 @@ namespace GWConquest public float Morale { - get => State.Morale; - set => State.Morale = value; + get => morale.Value; + set => morale.Value = value; } public int Fuel { - get => State.Fuel; - set => State.Fuel = value; + get => fuel.Value; + set => fuel.Value = value; } public int Food { - get => State.Food; - set => State.Food = value; + get => food.Value; + set => food.Value = value; } public int Supplies { - get => State.Supplies; - set => State.Supplies = value; + get => supplies.Value; + set => supplies.Value = value; } public Player Player { @@ -99,10 +118,10 @@ namespace GWConquest } public BattleUnitState BattleState { - get => (BattleUnitState) State.BattleState; + get => battleState.Value; set { - State.BattleState = (int) value; - if(BoltNetwork.IsServer) + battleState.Value = value; + if(IsServer) { if(CurrentBattle != null) { @@ -113,10 +132,10 @@ namespace GWConquest } public RevealState RevealState { - get => (RevealState) State.RevealState; + get => revealState.Value; set { - State.RevealState = (int) value; - if(BoltNetwork.IsServer) + revealState.Value = value; + if(IsServer) { if(CurrentBattle != null) { @@ -127,11 +146,11 @@ namespace GWConquest } public float ActionCooldown { - get => State.ActionCooldown; + get => actionCooldown.Value; } public float ActionCooldownPercent { - get => State.ActionCooldown / State.ActionCooldownMax; + get => actionCooldown.Value / actionCooldownMax.Value; } public BattleFlank FlankTarget { @@ -141,21 +160,26 @@ namespace GWConquest return null; } else { - int ind = State.FlankTarget; + int ind = flankTarget.Value; if(ind >= 0 && ind < CurrentBattle.FlankCount) { - return CurrentBattle.GetFlank(ind); + return CurrentBattle.GetFlank((ushort) ind); } else return null; } } - set => State.FlankTarget = value == null ? -1 : value.FlankId; + set => flankTarget.Value = value == null ? -1 : value.FlankId; } public void SetActionCooldown(float value) { - State.ActionCooldown = value; - State.ActionCooldownMax = value; + actionCooldown.Value = value; + actionCooldownMax.Value = value; + } + + public void ResetActionCooldown() + { + actionCooldown.Value = 0; } [System.NonSerialized] @@ -184,36 +208,49 @@ namespace GWConquest public Inventory Inventory; public Inventory Equipment; - public override void Attached() + private void OnFormationChanged(NetworkBehaviourReference previousValue, NetworkBehaviourReference newValue) { - Inventory = new Inventory(State, "Inventory"); - Equipment = new Inventory(State, "Equipment"); - - State.AddCallback("Formation", () => + Formation newFormation = newValue.GetBehaviour(); + if(newFormation != null) { - if(Formation != null) - { - Formation.OnUnitAdded(this); - } - }); + newFormation.OnUnitAdded(this); + } + } - State.AddCallback("UnitClass", () => { - Inventory.StorageCapacity = Class.InventorySlots; - Equipment.StorageCapacity = Class.EquipmentSlots; + private void OnUnitClassChanged(ushort oldID, ushort newID) + { + UnitClass newClass = UnitClass.FromID(newID); + Inventory.StorageCapacity = newClass.InventorySlots; + Equipment.StorageCapacity = newClass.EquipmentSlots; - if(entity.IsOwner) - { - Fuel = Class.FuelCapacity; - Food = Class.FoodCapacity; - Supplies = Class.SuppliesCapacity; - } - }); + if(IsOwner) + { + Fuel = newClass.FuelCapacity; + Food = newClass.FoodCapacity; + Supplies = newClass.SuppliesCapacity; + } + } + + public override void OnNetworkSpawn() + { + formation.OnValueChanged += OnFormationChanged; + unitClass.OnValueChanged += OnUnitClassChanged; - if(BoltNetwork.IsServer) + if(IsServer) { - State.FlankTarget = -1; + flankTarget.Value = -1; } + base.OnNetworkSpawn(); + } + + public void InstantiateWithClass(UnitClass uc) + { + Class = uc; + gameObject.name = uc.ShortName + "_" + NetworkObjectId.ToString(); + Hitpoints = uc.Hitpoints; + Shields = uc.Shields; + Morale = uc.Morale; } public static Unit SpawnUnit(Zone zone, UnitClass uc, Player player) @@ -221,31 +258,15 @@ namespace GWConquest Formation playerFormation = zone.Formations.FirstOrDefault(f => f.Player == player); if (playerFormation == null) { - BoltEntity formationEntity = BoltNetwork.Instantiate(BoltPrefabs.Formation); - player.AssignControl(formationEntity); - playerFormation = formationEntity.GetComponent(); - playerFormation.gameObject.name = "Formation_" + formationEntity.NetworkId.PackedValue.ToString(); - playerFormation.Player = player; - playerFormation.currentZone = zone; - playerFormation.ZoneType = uc.ZoneType; - - if(playerFormation.ZoneType == ZoneType.Space) - { - Vector3 startPos = zone.planet.GetFleetRestingPosition(); - playerFormation.State.FleetRestingPoint = startPos; - } + playerFormation = Formation.SpawnFormation(zone, player); } BoltEntity unitEntity = BoltNetwork.Instantiate(BoltPrefabs.Unit); player.AssignControl(unitEntity); Unit unit = unitEntity.GetComponent(); - unit.Class = uc; - unit.gameObject.name = uc.ShortName + "_" + unitEntity.NetworkId.PackedValue.ToString(); - unit.State.Hitpoints = uc.Hitpoints; - unit.State.Shields = uc.Shields; - unit.State.Morale = uc.Morale; + unit.InstantiateWithClass(uc); unit.Formation = playerFormation; - playerFormation.UnitEntities.Add(unitEntity); + playerFormation.AddUnit(unit); BoltLog.Info("Spawned unit {0} for player {1} in formation {2}", unit, player, playerFormation); @@ -417,7 +438,7 @@ namespace GWConquest } } - public override void SimulateOwner() + public void FixedUpdate() { var battle = CurrentBattle; if(!IsDead && battle != null && !battle.IsInPreparing) @@ -432,7 +453,7 @@ namespace GWConquest if (ActionCooldown <= 0) { - State.ActionCooldown = 0; + actionCooldown.Value = 0; if (BattleState == BattleUnitState.MovingToFlank) { BattleState = BattleUnitState.OnFlank; @@ -467,12 +488,12 @@ namespace GWConquest } else { - State.ActionCooldown -= BoltNetwork.FrameDeltaTime; + actionCooldown.Value -= Time.fixedDeltaTime; } if(ShieldsCooldown > 0) { - ShieldsCooldown -= BoltNetwork.FrameDeltaTime; + ShieldsCooldown -= Time.fixedDeltaTime; if(ShieldsCooldown <= 0) { ShieldsCooldown = 0; @@ -577,7 +598,7 @@ namespace GWConquest } public int TotalFuel { - get => Fuel + Inventory.GetItemAmount("Fuel"); + get => Fuel + Inventory.GetItemAmount(ItemRegistry.kFuel); } public void ConsumeTotalFuel(int amount) @@ -591,13 +612,13 @@ namespace GWConquest else { int diff = amount - Fuel; Fuel = 0; - Inventory.RemoveItem("Fuel", diff); + Inventory.RemoveItem(ItemRegistry.kFuel, diff); //Refill - int remaining = Inventory.GetItemAmount("Fuel"); + int remaining = Inventory.GetItemAmount(ItemRegistry.kFuel); int refill = Mathf.Min(remaining, Class.FuelCapacity); Fuel = refill; - Inventory.RemoveItem("Fuel", refill); + Inventory.RemoveItem(ItemRegistry.kFuel, refill); } } } diff --git a/Assets/GWConquest/Scripts/UnitClass.cs b/Assets/GWConquest/Scripts/UnitClass.cs index 901e820..a393567 100644 --- a/Assets/GWConquest/Scripts/UnitClass.cs +++ b/Assets/GWConquest/Scripts/UnitClass.cs @@ -10,10 +10,13 @@ namespace GWConquest public class UnitClass : IBuildable { private static Dictionary classMap; + private static UnitClass[] unitClasses; + private static ushort nextID = 0; public static void ClearRegistry() { classMap = null; + nextID = 0; } public static void LoadClassMap() @@ -29,7 +32,10 @@ namespace GWConquest TextAsset heroDatabase = Resources.Load("Database/Heroes"); LoadHeroDatabase(heroDatabase); - GameManager.Instance.UnitClasses = classMap.Values.ToArray(); + var classList = classMap.Values.ToList(); + classList.Sort((uc1, uc2) => uc1.ID - uc2.ID); + unitClasses = classList.ToArray(); + GameManager.Instance.UnitClasses = unitClasses; BoltLog.Info("All unit classes loaded."); } @@ -164,6 +170,9 @@ namespace GWConquest unitClass.LoadTexture(); + unitClass.ID = nextID; + nextID++; + classMap.Add(unitClass.ShortName, unitClass); } catch (Exception ex) @@ -250,6 +259,9 @@ namespace GWConquest unitClass.LoadTexture(); + unitClass.ID = nextID; + nextID++; + classMap.Add(unitClass.ShortName, unitClass); } catch(Exception ex) @@ -282,6 +294,22 @@ namespace GWConquest } } + public static UnitClass FromID(ushort id) + { + if (unitClasses == null) + { + LoadClassMap(); + } + if(id < unitClasses.Length) + { + return unitClasses[id]; + } + else { + Debug.LogWarningFormat("Unit class ID {0} not in list!", id); + return null; + } + } + public static IEnumerable AllUnitClasses { get { @@ -293,6 +321,7 @@ namespace GWConquest } } + public ushort ID {get; private set;} public string FullName {get; private set;} public string ShortName {get; private set;} public Sprite Sprite @@ -413,7 +442,7 @@ namespace GWConquest } } else { - if(!inventory.HasItem(cost.Item, cost.Amount)) + if(!inventory.HasItem(ItemRegistry.Instance.IDFromName(cost.Item), cost.Amount)) { return false; } @@ -425,6 +454,7 @@ namespace GWConquest public interface IBuildable { + ushort ID {get;} BuildCost BuildCost {get;} float BuildTime {get;} Sprite Sprite {get;} diff --git a/Assets/GWConquest/Scripts/Util.cs b/Assets/GWConquest/Scripts/Util.cs index 612f47d..77dd55f 100644 --- a/Assets/GWConquest/Scripts/Util.cs +++ b/Assets/GWConquest/Scripts/Util.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using System.Text.RegularExpressions; +using Unity.Netcode; namespace GWConquest { @@ -311,6 +312,17 @@ namespace GWConquest && screenPoint.y >= -tolerance && screenPoint.y <= 1f + tolerance; } + public static T GetBehaviour(this NetworkBehaviourReference reference) where T : NetworkBehaviour + { + if(reference.TryGet(out NetworkBehaviour beh)) + { + return beh as T; + } + else { + return null; + } + } + } } \ No newline at end of file diff --git a/Packages/manifest.json b/Packages/manifest.json index 3cf6608..8beb60e 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -7,6 +7,7 @@ "com.unity.ide.rider": "3.0.24", "com.unity.ide.visualstudio": "2.0.18", "com.unity.ide.vscode": "1.2.5", + "com.unity.netcode.gameobjects": "1.5.1", "com.unity.postprocessing": "3.2.2", "com.unity.test-framework": "1.1.33", "com.unity.timeline": "1.7.4", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index da72641..d6f21cb 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -44,6 +44,16 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.collections": { + "version": "1.2.4", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.6.6", + "com.unity.test-framework": "1.1.31" + }, + "url": "https://packages.unity.com" + }, "com.unity.ext.nunit": { "version": "1.0.6", "depth": 1, @@ -83,6 +93,23 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.netcode.gameobjects": { + "version": "1.5.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.nuget.mono-cecil": "1.10.1", + "com.unity.transport": "1.3.4" + }, + "url": "https://packages.unity.com" + }, + "com.unity.nuget.mono-cecil": { + "version": "1.11.4", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.postprocessing": { "version": "3.2.2", "depth": 0, @@ -115,6 +142,17 @@ }, "url": "https://packages.unity.com" }, + "com.unity.transport": { + "version": "1.3.4", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.collections": "1.2.4", + "com.unity.burst": "1.6.6", + "com.unity.mathematics": "1.2.6" + }, + "url": "https://packages.unity.com" + }, "com.unity.ugui": { "version": "1.0.0", "depth": 0,