Browse Source

(WIP) Unity Netcode Part 2

master
laurids 1 year ago
parent
commit
1eb6b3e9e6
10 changed files with 175 additions and 155 deletions
  1. +6
    -1
      Assets/GWConquest/Scripts/District.cs
  2. +7
    -0
      Assets/GWConquest/Scripts/DistrictFactory.cs
  3. +31
    -25
      Assets/GWConquest/Scripts/Formation.cs
  4. +1
    -1
      Assets/GWConquest/Scripts/GWNetworkArray.cs
  5. +24
    -0
      Assets/GWConquest/Scripts/Inventory.cs
  6. +6
    -0
      Assets/GWConquest/Scripts/Player.cs
  7. +22
    -82
      Assets/GWConquest/Scripts/ServerCallbacks.cs
  8. +4
    -4
      Assets/GWConquest/Scripts/UI/BattleUnitIcon.cs
  9. +63
    -42
      Assets/GWConquest/Scripts/Unit.cs
  10. +11
    -0
      Assets/GWConquest/Scripts/Util.cs

+ 6
- 1
Assets/GWConquest/Scripts/District.cs View File

@ -8,7 +8,7 @@ using Unity.Collections;
namespace GWConquest
{
public class District : NetworkBehaviour
public class District : NetworkBehaviour, IHasInventory
{
public static List<District> AllDistricts = new List<District>();
public static List<DistrictConnection> AllConnections = new List<DistrictConnection>();
@ -333,6 +333,11 @@ namespace GWConquest
upgrades.Add(upgradeID);
}
public IInventory GetInventory()
{
return Inventory;
}
}
public enum DistrictType


+ 7
- 0
Assets/GWConquest/Scripts/DistrictFactory.cs View File

@ -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<Player>());
}
public float GetBuildTimeForBuildable(IBuildable buildable)
{
return buildable.BuildTime * GameManager.Instance.TimeScale;


+ 31
- 25
Assets/GWConquest/Scripts/Formation.cs View File

@ -25,7 +25,7 @@ namespace GWConquest
}
public class Formation : NetworkBehaviour, IMovable<Zone>
public class Formation : NetworkBehaviour, IMovable<Zone>, IHasInventory
{
public static List<Formation> AllFormations = new List<Formation>();
@ -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<Formation>());
}
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;
}
}


+ 1
- 1
Assets/GWConquest/Scripts/GWNetworkArray.cs View File

@ -1,5 +1,5 @@
using Unity.Netcode;
namespace GWConquest {
}

+ 24
- 0
Assets/GWConquest/Scripts/Inventory.cs View File

@ -172,6 +172,11 @@ namespace GWConquest
bool RemoveItem(ushort item, int amount);
}
public interface IHasInventory
{
IInventory GetInventory();
}
public class Inventory : GWNetworkList<ItemStack>, 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


+ 6
- 0
Assets/GWConquest/Scripts/Player.cs View File

@ -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]


+ 22
- 82
Assets/GWConquest/Scripts/ServerCallbacks.cs View File

@ -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<ServerCallbacks>();
}
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<Player>();
Unit.SpawnUnit(zone, unitClass, player);
}
public override void OnEvent(MoveFormationEvent evnt)
{
Formation formation = evnt.Formation.GetComponent<Formation>();
var targetFormation = evnt.TargetFormation?.GetComponent<Formation>();
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<DistrictFactory>();
IBuildable buildable = evnt.IsUpgrade ? DistrictUpgrade.FromName(evnt.UnitClass) : UnitClass.FromName(evnt.UnitClass);
factory.AddProductionQueueEntry(buildable, evnt.Player.GetComponent<Player>());
}
public override void OnEvent(MoveUnitToFlankEvent evnt)
{
var flank = evnt.Flank.GetComponent<BattleFlank>();
flank.Battle.MoveUnitToFlank(evnt.Unit.GetComponent<Unit>(), flank, evnt.FlankIndex);
}
public override void OnEvent(MoveUnitToReserveEvent evnt)
{
var unit = evnt.Unit.GetComponent<Unit>();
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<Formation>();
var unit = evnt.Unit?.GetComponent<Unit>();
formation.HeroUnit = unit;
}
NetworkBehaviour originBeh = origin.GetBehaviour();
NetworkBehaviour targetBeh = target.GetBehaviour();
public override void OnEvent(StartShellingEvent evnt)
{
var unit = evnt.Unit.GetComponent<Unit>();
var flank = evnt.BattleFlank?.GetComponent<BattleFlank>();
unit.StartShelling(flank);
}
public override void OnEvent(MoveItemEvent evnt)
{
IInventory origin = null;
if(evnt.Origin.GetComponent<District>() != null)
if(originBeh is IHasInventory && targetBeh is IHasInventory)
{
origin = evnt.Origin.GetComponent<District>().Inventory;
}
else if(evnt.Origin.GetComponent<Formation>() != null) {
origin = evnt.Origin.GetComponent<Formation>().FormationInventory;
}
IInventory target = null;
if(evnt.Target.GetComponent<District>() != null)
{
target = evnt.Target.GetComponent<District>().Inventory;
}
else if(evnt.Target.GetComponent<Formation>() != null) {
target = evnt.Target.GetComponent<Formation>().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");
}
}
}

+ 4
- 4
Assets/GWConquest/Scripts/UI/BattleUnitIcon.cs View File

@ -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<Unit>().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<Unit>().Class.UnitType == UnitType.Artillery)
bool explode = (attacker != null && attacker.Class.UnitType == UnitType.Artillery)
|| Unit.Class.UnitType != UnitType.Infantry;
StartCoroutine(DeathAnimCoroutine(explode));
}


+ 63
- 42
Assets/GWConquest/Scripts/Unit.cs View File

@ -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<Unit>());
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<Unit>());
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<Unit>();
WeaponType weaponType = (WeaponType)evnt.WeaponType;
if(target != null && evnt.Hit)
if(target.HasValue && hit)
{
CurrentIcon.GetComponentInParent<BattleUI>().SpawnShotEffect(CurrentIcon, target.CurrentIcon, weaponType);
CurrentIcon.GetComponentInParent<BattleUI>().SpawnShotEffect(CurrentIcon, target.GetBehaviour<Unit>().CurrentIcon, weaponType);
}
else {
CurrentIcon.GetComponentInParent<BattleUI>().SpawnShotEffect(CurrentIcon, null, weaponType);
@ -423,15 +437,14 @@ namespace GWConquest
}
else if(Class.ZoneType == ZoneType.Space && IngameUI.PlanetViewEnabled)
{
var target = evnt.Target?.GetComponent<Unit>();
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<Unit>());
}
}
}
@ -547,6 +560,12 @@ namespace GWConquest
SetActionCooldown(GameManager.Instance.ArtilleryCooldown / movement);
}
[ServerRpc]
public void StartShellingRpc(NetworkBehaviourReference target)
{
StartShelling(target.GetBehaviour<BattleFlank>());
}
public float MoraleDamageFactor {
get => 0.8f;
}
@ -624,4 +643,6 @@ namespace GWConquest
}
}
}

+ 11
- 0
Assets/GWConquest/Scripts/Util.cs View File

@ -323,6 +323,17 @@ namespace GWConquest
}
}
public static NetworkBehaviour GetBehaviour(this NetworkBehaviourReference reference)
{
if(reference.TryGet(out NetworkBehaviour beh))
{
return beh;
}
else {
return null;
}
}
}
}

Loading…
Cancel
Save