From 1915ed75e3311b774e42cb28101b4b5de5313c0a Mon Sep 17 00:00:00 2001 From: "Dolzhukov, Viktor" Date: Mon, 26 May 2025 22:30:44 +0500 Subject: [PATCH 1/2] Npc Invasion --- .../Modules/NpcsModule.cs | 2 + .../Modules/TerrainsModule.cs | 7 +- .../Modules/ZonesModule.cs | 3 +- .../PerpetuumBootstrapper.cs | 1 + src/Perpetuum/Perpetuum.csproj | 10 +- .../EventMessages/SapAttackersSpawnMessage.cs | 25 ++++ .../NpcReinforcementSpawner.cs | 61 +++++----- .../NpcSpawnEventHandler.cs | 38 ++++--- .../NpcSpawnEventHandlers/OreNPCSpawner.cs | 32 ++++-- .../SapAttackerSpawner.cs | 107 ++++++++++++++++++ .../Services/EventServices/EventType.cs | 1 + src/Perpetuum/Units/Unit.cs | 6 + .../Zones/Intrusion/DestructionSAP.cs | 8 ++ src/Perpetuum/Zones/Intrusion/Outpost.cs | 22 ++++ src/Perpetuum/Zones/Intrusion/SAP.cs | 46 +++++++- .../NpcSystem/AI/BodyPullThreatHelper.cs | 9 +- .../AI/CombatDrones/GuardCombatDroneAI.cs | 2 +- src/Perpetuum/Zones/NpcSystem/Creature.cs | 3 +- src/Perpetuum/Zones/NpcSystem/Faction.cs | 1 + src/Perpetuum/Zones/NpcSystem/INpcPresence.cs | 26 +++++ .../Zones/NpcSystem/INpcPresences.cs | 17 +++ src/Perpetuum/Zones/NpcSystem/Npc.cs | 10 +- .../Reinforcements/INpcReinforcementWave.cs | 33 ------ .../Reinforcements/INpcReinforcements.cs | 37 ------ .../INpcReinforcementsRepository.cs | 4 +- .../Reinforcements/NpcReinforcementWave.cs | 5 +- .../Reinforcements/NpcReinforcements.cs | 17 ++- .../NpcReinforcementsRepository.cs | 27 +++-- .../SapAttackers/ISapAttackersRepository.cs | 7 ++ .../SapAttackers/SapAttackerPresence.cs | 42 +++++++ .../NpcSystem/SapAttackers/SapAttackers.cs | 62 ++++++++++ .../SapAttackers/SapAttackersRepository.cs | 33 ++++++ 32 files changed, 556 insertions(+), 148 deletions(-) create mode 100644 src/Perpetuum/Services/EventServices/EventMessages/SapAttackersSpawnMessage.cs create mode 100644 src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/SapAttackerSpawner.cs create mode 100644 src/Perpetuum/Zones/NpcSystem/INpcPresence.cs create mode 100644 src/Perpetuum/Zones/NpcSystem/INpcPresences.cs delete mode 100644 src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementWave.cs delete mode 100644 src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcements.cs create mode 100644 src/Perpetuum/Zones/NpcSystem/SapAttackers/ISapAttackersRepository.cs create mode 100644 src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackerPresence.cs create mode 100644 src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackers.cs create mode 100644 src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackersRepository.cs diff --git a/src/Perpetuum.Bootstrapper/Modules/NpcsModule.cs b/src/Perpetuum.Bootstrapper/Modules/NpcsModule.cs index fb24c1f74..7a5c39543 100644 --- a/src/Perpetuum.Bootstrapper/Modules/NpcsModule.cs +++ b/src/Perpetuum.Bootstrapper/Modules/NpcsModule.cs @@ -13,6 +13,7 @@ using Perpetuum.Zones.NpcSystem.Presences.RandomExpiringPresence; using Perpetuum.Zones.NpcSystem.Reinforcements; using Perpetuum.Zones.NpcSystem.SafeSpawnPoints; +using Perpetuum.Zones.NpcSystem.SapAttackers; using System; namespace Perpetuum.Bootstrapper.Modules @@ -24,6 +25,7 @@ protected override void Load(ContainerBuilder builder) _ = builder.RegisterType().As(); _ = builder.RegisterType().SingleInstance(); _ = builder.RegisterType().SingleInstance().As(); + _ = builder.RegisterType().SingleInstance().As(); _ = builder.RegisterType().As(); _ = builder.RegisterType(); diff --git a/src/Perpetuum.Bootstrapper/Modules/TerrainsModule.cs b/src/Perpetuum.Bootstrapper/Modules/TerrainsModule.cs index 0ef8e7bfd..cb49723c8 100644 --- a/src/Perpetuum.Bootstrapper/Modules/TerrainsModule.cs +++ b/src/Perpetuum.Bootstrapper/Modules/TerrainsModule.cs @@ -4,6 +4,7 @@ using Perpetuum.Threading.Process; using Perpetuum.Zones; using Perpetuum.Zones.NpcSystem.Reinforcements; +using Perpetuum.Zones.NpcSystem.SapAttackers; using Perpetuum.Zones.Scanning.Scanners; using Perpetuum.Zones.Terrains; using Perpetuum.Zones.Terrains.Materials; @@ -26,9 +27,11 @@ protected override void Load(ContainerBuilder builder) return zone => { IMineralConfigurationReader reader = ctx.Resolve(); - OreNpcSpawner listener = new OreNpcSpawner(zone, ctx.Resolve(), reader); + OreNpcSpawner oreNpcSpawnlistener = new OreNpcSpawner(zone, ctx.Resolve(), ctx.Resolve(), reader); + SapAttackerSpawner sapAttackerSpawnlistener = new SapAttackerSpawner(zone, ctx.Resolve(), ctx.Resolve()); EventListenerService eventListenerService = ctx.Resolve(); - eventListenerService.AttachListener(listener); + eventListenerService.AttachListener(oreNpcSpawnlistener); + eventListenerService.AttachListener(sapAttackerSpawnlistener); if (zone is TrainingZone) { GravelRepository repo = ctx.Resolve(); diff --git a/src/Perpetuum.Bootstrapper/Modules/ZonesModule.cs b/src/Perpetuum.Bootstrapper/Modules/ZonesModule.cs index 2a4e5a2db..6abc97c11 100644 --- a/src/Perpetuum.Bootstrapper/Modules/ZonesModule.cs +++ b/src/Perpetuum.Bootstrapper/Modules/ZonesModule.cs @@ -22,6 +22,7 @@ using Perpetuum.Zones.NpcSystem.Presences; using Perpetuum.Zones.NpcSystem.Reinforcements; using Perpetuum.Zones.NpcSystem.SafeSpawnPoints; +using Perpetuum.Zones.NpcSystem.SapAttackers; using Perpetuum.Zones.PBS; using Perpetuum.Zones.Scanning.Results; using Perpetuum.Zones.Teleporting; @@ -172,7 +173,7 @@ void RegisterZone(ZoneType type) where T : Zone zone.PlayerStateManager = ctx.Resolve>().Invoke(zone); } - ctx.Resolve().AttachListener(new NpcReinforcementSpawner(zone, ctx.Resolve())); + ctx.Resolve().AttachListener(new NpcReinforcementSpawner(zone, ctx.Resolve(), ctx.Resolve())); WeatherEventListener listener = ctx.Resolve>().Invoke(zone); listener.Subscribe(zone.Weather); diff --git a/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs b/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs index 6ef3a85f7..97d8b919a 100644 --- a/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs +++ b/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs @@ -556,6 +556,7 @@ private void InitRelayManager() _ = _builder.RegisterType(); _ = _builder.RegisterType().As>(); _ = _builder.RegisterType().As>(); + _ = _builder.RegisterType().As>(); _ = _builder.RegisterType().SingleInstance().OnActivated(e => { e.Context.Resolve().AddProcess(e.Instance.ToAsync().AsTimed(TimeSpan.FromSeconds(0.75))); diff --git a/src/Perpetuum/Perpetuum.csproj b/src/Perpetuum/Perpetuum.csproj index 8fdb07254..d9e75ae0d 100644 --- a/src/Perpetuum/Perpetuum.csproj +++ b/src/Perpetuum/Perpetuum.csproj @@ -348,12 +348,14 @@ + + @@ -766,9 +768,15 @@ + + + + + + @@ -876,9 +884,7 @@ - - diff --git a/src/Perpetuum/Services/EventServices/EventMessages/SapAttackersSpawnMessage.cs b/src/Perpetuum/Services/EventServices/EventMessages/SapAttackersSpawnMessage.cs new file mode 100644 index 000000000..f34a844e9 --- /dev/null +++ b/src/Perpetuum/Services/EventServices/EventMessages/SapAttackersSpawnMessage.cs @@ -0,0 +1,25 @@ +using Perpetuum.Zones.Intrusion; + +namespace Perpetuum.Services.EventServices.EventMessages +{ + public class SapAttackersSpawnMessage : IEventMessage + { + public EventType Type => EventType.NpcSapAttackers; + + public SapState SapState { get; } + + public SAP Sap { get; } + + public int ZoneId { get; } + + public int Stability { get; set; } + + public SapAttackersSpawnMessage(SAP sap, SapState sapState, int zoneID, int stability) + { + ZoneId = zoneID; + Sap = sap; + SapState = sapState; + Stability = stability; + } + } +} diff --git a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcReinforcementSpawner.cs b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcReinforcementSpawner.cs index dc8788f37..984ee8731 100644 --- a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcReinforcementSpawner.cs +++ b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcReinforcementSpawner.cs @@ -6,6 +6,7 @@ using Perpetuum.Zones.NpcSystem.Flocks; using Perpetuum.Zones.NpcSystem.Presences; using Perpetuum.Zones.NpcSystem.Reinforcements; +using Perpetuum.Zones.NpcSystem.SapAttackers; using System; using System.Collections.Generic; using System.Linq; @@ -14,16 +15,19 @@ namespace Perpetuum.Services.EventServices.EventProcessors.NpcSpawnEventHandlers { public class NpcReinforcementSpawner : NpcSpawnEventHandler { - protected override TimeSpan SPAWN_DELAY { get { return TimeSpan.FromSeconds(5); } } - protected override TimeSpan SPAWN_LIFETIME { get { return TimeSpan.FromMinutes(30); } } - protected override int MAX_SPAWN_DIST { get { return 15; } } + protected override TimeSpan SPAWN_DELAY => TimeSpan.FromSeconds(5); + protected override TimeSpan SPAWN_LIFETIME => TimeSpan.FromMinutes(30); + protected override int MAX_SPAWN_DIST => 15; public override EventType Type => EventType.NpcReinforce; - private readonly IDictionary _reinforcementsByNpc = new Dictionary(); - public NpcReinforcementSpawner(IZone zone, INpcReinforcementsRepository reinforcementsRepo) : base(zone, reinforcementsRepo) { } + private readonly IDictionary _reinforcementsByNpc = new Dictionary(); + public NpcReinforcementSpawner( + IZone zone, + INpcReinforcementsRepository reinforcementsRepo, + ISapAttackersRepository sapAttackersRepository) : base(zone, reinforcementsRepo, sapAttackersRepository) { } - protected override IEnumerable GetActiveReinforcments(Presence presence) + protected override IEnumerable GetActiveReinforcments(Presence presence) { return _reinforcementsByNpc.Where(p => p.Value.HasActivePresence(presence)).Select(p => p.Value); } @@ -33,21 +37,23 @@ protected override bool CheckMessage(IEventMessage inMsg, out NpcReinforcementsM if (inMsg is NpcReinforcementsMessage message && _zone.Id == message.ZoneId) { msg = message; + return true; } else { msg = null; + return false; } } protected override void CheckReinforcements(NpcReinforcementsMessage msg) { - var info = msg.SmartCreature.BossInfo; + NpcBossInfo info = msg.SmartCreature.BossInfo; if (!_reinforcementsByNpc.ContainsKey(info)) { - var reinforcements = _npcReinforcementsRepo.CreateNpcBossAddSpawn(info, msg.ZoneId); + INpcPresences reinforcements = _npcReinforcementsRepo.CreateNpcBossAddSpawn(info, msg.ZoneId); _reinforcementsByNpc.Add(info, reinforcements); } } @@ -56,54 +62,55 @@ protected override bool CheckState(NpcReinforcementsMessage msg) { if (msg.SmartCreature.BossInfo.IsDead) { - CleanupAllReinforcements(msg); + CleanupAllAttackers(msg); + return true; } UpdateAggro(msg); + return false; } private void UpdateAggro(NpcReinforcementsMessage msg) { - var info = msg.SmartCreature.BossInfo; + NpcBossInfo info = msg.SmartCreature.BossInfo; if (_reinforcementsByNpc.ContainsKey(info)) { - var activeWaves = _reinforcementsByNpc[info].GetAllActiveWaves().Where(w => w.ActivePresence != null); - foreach (var wave in activeWaves) + IEnumerable activeWaves = _reinforcementsByNpc[info].GetAllActivePresences().Where(w => w.ActivePresence != null); + foreach (INpcPresence wave in activeWaves) { SpreadAggro(wave.ActivePresence, msg.SmartCreature); } } } - protected override void CleanupAllReinforcements(NpcReinforcementsMessage msg) + protected override void CleanupAllAttackers(NpcReinforcementsMessage msg) { - var info = msg.SmartCreature.BossInfo; + NpcBossInfo info = msg.SmartCreature.BossInfo; if (_reinforcementsByNpc.ContainsKey(info)) { - var activeWaves = _reinforcementsByNpc[info].GetAllActiveWaves(); - foreach (var wave in activeWaves) + INpcPresence[] activeWaves = _reinforcementsByNpc[info].GetAllActivePresences(); + foreach (INpcPresence wave in activeWaves) { ExpireWave(wave); } + _reinforcementsByNpc.Remove(info); } } protected override Position FindSpawnPosition(NpcReinforcementsMessage msg, int maxRange) { - var finder = new RandomWalkableAroundPositionFinder(_zone, msg.SmartCreature.CurrentPosition, maxRange); - if (finder.Find(out Position result)) - { - return result; - } - return msg.SmartCreature.CurrentPosition; + RandomWalkableAroundPositionFinder finder = new RandomWalkableAroundPositionFinder(_zone, msg.SmartCreature.CurrentPosition, maxRange); + + return finder.Find(out Position result) ? result : msg.SmartCreature.CurrentPosition; } - protected override INpcReinforcementWave GetNextWave(NpcReinforcementsMessage msg) + protected override INpcPresence GetNextWave(NpcReinforcementsMessage msg) { - var npc = msg.SmartCreature; - var percent = 1.0 - npc.ArmorPercentage; + SmartCreature npc = msg.SmartCreature; + double percent = 1.0 - npc.ArmorPercentage; + return _reinforcementsByNpc[npc.BossInfo].GetNextPresence(percent); } @@ -114,9 +121,9 @@ protected override void OnSpawning(Presence pres, NpcReinforcementsMessage msg) private void SpreadAggro(Presence presenceToAggro, SmartCreature smartCreatureWithAggro) { - foreach (var npc in presenceToAggro.Flocks.GetMembers()) + foreach (Npc npc in presenceToAggro.Flocks.GetMembers()) { - foreach (var threat in smartCreatureWithAggro.ThreatManager.Hostiles) + foreach (Zones.NpcSystem.ThreatManaging.Hostile threat in smartCreatureWithAggro.ThreatManager.Hostiles) { npc.AddDirectThreat(threat.Unit, threat.Threat + FastRandom.NextDouble(5, 10)); } diff --git a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcSpawnEventHandler.cs b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcSpawnEventHandler.cs index f89c735a5..f9c41f6f0 100644 --- a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcSpawnEventHandler.cs +++ b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/NpcSpawnEventHandler.cs @@ -2,8 +2,10 @@ using Perpetuum.Log; using Perpetuum.Services.EventServices.EventMessages; using Perpetuum.Zones; +using Perpetuum.Zones.NpcSystem; using Perpetuum.Zones.NpcSystem.Presences; using Perpetuum.Zones.NpcSystem.Reinforcements; +using Perpetuum.Zones.NpcSystem.SapAttackers; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -22,13 +24,15 @@ public abstract class NpcSpawnEventHandler : EventProcessor where T : IEventM protected readonly IZone _zone; protected readonly INpcReinforcementsRepository _npcReinforcementsRepo; - public NpcSpawnEventHandler(IZone zone, INpcReinforcementsRepository reinforcementsRepo) + protected readonly ISapAttackersRepository _npcSapAttackersRepo; + public NpcSpawnEventHandler(IZone zone, INpcReinforcementsRepository reinforcementsRepo, ISapAttackersRepository sapAttackersRepository) { _zone = zone; _npcReinforcementsRepo = reinforcementsRepo; + _npcSapAttackersRepo = sapAttackersRepository; } - protected abstract IEnumerable GetActiveReinforcments(Presence presence); + protected abstract IEnumerable GetActiveReinforcments(Presence presence); protected abstract bool CheckMessage(IEventMessage inMsg, out T msg); @@ -36,11 +40,11 @@ public NpcSpawnEventHandler(IZone zone, INpcReinforcementsRepository reinforceme protected abstract bool CheckState(T msg); - protected abstract void CleanupAllReinforcements(T msg); + protected abstract void CleanupAllAttackers(T msg); protected abstract Position FindSpawnPosition(T msg, int maxRange); - protected abstract INpcReinforcementWave GetNextWave(T msg); + protected abstract INpcPresence GetNextWave(T msg); protected virtual void OnSpawning(Presence pres, T msg) { } @@ -55,9 +59,9 @@ protected void DoBeams(Position beamLocation) _zone.CreateBeam(BeamType.teleport_storm, b => b.WithPosition(beamLocation).WithDuration(SPAWN_DELAY)); } - protected void DoSpawning(INpcReinforcementWave wave, Position homePosition, Position spawnPosition, T msg) + protected void DoSpawning(INpcPresence wave, Position homePosition, Position spawnPosition, T msg) { - var pres = _zone.AddDynamicPresenceToPosition(wave.PresenceId, homePosition, spawnPosition, SPAWN_LIFETIME); + DynamicPresenceExtended pres = _zone.AddDynamicPresenceToPosition(wave.PresenceId, homePosition, spawnPosition, SPAWN_LIFETIME); OnSpawning(pres, msg); pres.PresenceExpired += OnPresenceExpired; wave.SetActivePresence(pres); @@ -65,15 +69,15 @@ protected void DoSpawning(INpcReinforcementWave wave, Position homePosition, Pos protected void OnPresenceExpired(Presence presence) { - var activeReinforcements = GetActiveReinforcments(presence); - foreach (var reinforcements in activeReinforcements) + IEnumerable activeReinforcements = GetActiveReinforcments(presence); + foreach (Zones.NpcSystem.INpcPresences reinforcements in activeReinforcements) { - var wave = reinforcements.GetActiveWaveOfPresence(presence); + INpcPresence wave = reinforcements.GetActivePresence(presence); ExpireWave(wave); } } - protected void ExpireWave(INpcReinforcementWave wave) + protected void ExpireWave(INpcPresence wave) { wave.ActivePresence.PresenceExpired -= OnPresenceExpired; wave.DeactivatePresence(); @@ -86,22 +90,30 @@ public override void HandleMessage(IEventMessage value) if (CheckMessage(value, out T msg)) { if (CheckState(msg)) + { return; + } if (_spawning) + { return; + } CheckReinforcements(msg); - var wave = GetNextWave(msg); + INpcPresence wave = GetNextWave(msg); if (wave == null) + { return; // Presence not found for this message state or already spawned + } - var spawnPos = FindSpawnPosition(msg, MAX_SPAWN_DIST); + Position spawnPos = FindSpawnPosition(msg, MAX_SPAWN_DIST); if (spawnPos == Position.Empty) + { return; // Failed to find valid spawn location, try again on next cycle + } - var homePos = GetHomePos(msg, spawnPos); + Position homePos = GetHomePos(msg, spawnPos); DoBeams(homePos); diff --git a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/OreNPCSpawner.cs b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/OreNPCSpawner.cs index 770a7906c..eb55e5c5b 100644 --- a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/OreNPCSpawner.cs +++ b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/OreNPCSpawner.cs @@ -1,8 +1,10 @@ using Perpetuum.Services.EventServices.EventMessages; using Perpetuum.Zones; using Perpetuum.Zones.Finders.PositionFinders; +using Perpetuum.Zones.NpcSystem; using Perpetuum.Zones.NpcSystem.Presences; using Perpetuum.Zones.NpcSystem.Reinforcements; +using Perpetuum.Zones.NpcSystem.SapAttackers; using Perpetuum.Zones.Terrains.Materials.Minerals; using System; using System.Collections.Generic; @@ -23,15 +25,19 @@ public class OreNpcSpawner : NpcSpawnEventHandler public override EventType Type => EventType.NpcOre; - private readonly IDictionary _reinforcementsByNode = new Dictionary(); + private readonly IDictionary _reinforcementsByNode = new Dictionary(); private readonly IEnumerable _mineralConfigs; - public OreNpcSpawner(IZone zone, INpcReinforcementsRepository reinforcementsRepo, IMineralConfigurationReader mineralConfigurationReader) : base(zone, reinforcementsRepo) + public OreNpcSpawner( + IZone zone, + INpcReinforcementsRepository reinforcementsRepo, + ISapAttackersRepository sapAttackersRepository, + IMineralConfigurationReader mineralConfigurationReader) : base(zone, reinforcementsRepo, sapAttackersRepository) { _mineralConfigs = mineralConfigurationReader.ReadAll().Where(c => c.ZoneId == zone.Id); } - protected override IEnumerable GetActiveReinforcments(Presence presence) + protected override IEnumerable GetActiveReinforcments(Presence presence) { return _reinforcementsByNode.Where(p => p.Value.HasActivePresence(presence)).Select(p => p.Value); } @@ -41,11 +47,13 @@ protected override bool CheckMessage(IEventMessage inMsg, out OreNpcSpawnMessage if (inMsg is OreNpcSpawnMessage message && _zone.Id == message.ZoneId) { msg = message; + return true; } else { msg = null; + return false; } } @@ -55,7 +63,7 @@ protected override void CheckReinforcements(OreNpcSpawnMessage msg) MineralNode node = msg.Node; if (!_reinforcementsByNode.ContainsKey(node)) { - INpcReinforcements oreSpawn = _npcReinforcementsRepo.CreateOreNPCSpawn(node.Type, msg.ZoneId); + INpcPresences oreSpawn = _npcReinforcementsRepo.CreateOreNPCSpawn(node.Type, msg.ZoneId); _reinforcementsByNode.Add(node, oreSpawn); } } @@ -64,22 +72,25 @@ protected override bool CheckState(OreNpcSpawnMessage msg) { if (msg.NodeState == OreNodeState.Removed) { - CleanupAllReinforcements(msg); + CleanupAllAttackers(msg); + return true; } + return false; } - protected override void CleanupAllReinforcements(OreNpcSpawnMessage msg) + protected override void CleanupAllAttackers(OreNpcSpawnMessage msg) { MineralNode node = msg.Node; if (_reinforcementsByNode.ContainsKey(node)) { - INpcReinforcementWave[] activeWaves = _reinforcementsByNode[node].GetAllActiveWaves(); - foreach (INpcReinforcementWave wave in activeWaves) + INpcPresence[] activeWaves = _reinforcementsByNode[node].GetAllActivePresences(); + foreach (INpcPresence wave in activeWaves) { ExpireWave(wave); } + _reinforcementsByNode.Remove(node); } } @@ -88,6 +99,7 @@ protected override Position FindSpawnPosition(OreNpcSpawnMessage msg, int maxRan { Position fieldCenter = msg.Node.Area.Center.ToPosition(); RandomWalkableOnCircle finder = new RandomWalkableOnCircle(_zone, fieldCenter, maxRange, MIN_SPAWN_DIST_TOLERANCE); + return finder.Find(out Position result) ? result : Position.Empty; } @@ -96,13 +108,15 @@ private double ComputeFieldPercentConsumed(MineralNode node) int current = Convert.ToInt32(node.GetTotalAmount()); int total = _mineralConfigs.Single(c => c.Type == node.Type).TotalAmountPerNode; double percent = 1.0 - (current / (double)total).Clamp(); + return percent; } - protected override INpcReinforcementWave GetNextWave(OreNpcSpawnMessage msg) + protected override INpcPresence GetNextWave(OreNpcSpawnMessage msg) { MineralNode node = msg.Node; double percent = ComputeFieldPercentConsumed(node); + return _reinforcementsByNode[node].GetNextPresence(percent); } diff --git a/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/SapAttackerSpawner.cs b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/SapAttackerSpawner.cs new file mode 100644 index 000000000..30274858d --- /dev/null +++ b/src/Perpetuum/Services/EventServices/EventProcessors/NpcSpawnEventHandlers/SapAttackerSpawner.cs @@ -0,0 +1,107 @@ +using Perpetuum.Services.EventServices.EventMessages; +using Perpetuum.Zones; +using Perpetuum.Zones.Finders.PositionFinders; +using Perpetuum.Zones.NpcSystem; +using Perpetuum.Zones.NpcSystem.Presences; +using Perpetuum.Zones.NpcSystem.Reinforcements; +using Perpetuum.Zones.NpcSystem.SapAttackers; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Perpetuum.Services.EventServices.EventProcessors.NpcSpawnEventHandlers +{ + public class SapAttackerSpawner : NpcSpawnEventHandler + { + protected override TimeSpan SPAWN_DELAY => TimeSpan.FromSeconds(10); + protected override TimeSpan SPAWN_LIFETIME => TimeSpan.FromHours(3); + protected override int MAX_SPAWN_DIST => 100; + + private const int MIN_SPAWN_DIST_TOLERANCE = 30; + + public override EventType Type => EventType.NpcSapAttackers; + + private readonly IDictionary _attackersBySap = new Dictionary(); + + public SapAttackerSpawner( + IZone zone, + INpcReinforcementsRepository reinforcementsRepo, + ISapAttackersRepository sapAttackersRepository) : base(zone, reinforcementsRepo, sapAttackersRepository) + { + } + + protected override IEnumerable GetActiveReinforcments(Presence presence) + { + return _attackersBySap.Where(p => p.Value.HasActivePresence(presence)).Select(p => p.Value); + } + + protected override bool CheckMessage(IEventMessage inMsg, out SapAttackersSpawnMessage msg) + { + if (inMsg is SapAttackersSpawnMessage message && _zone.Id == message.ZoneId) + { + msg = message; + + return true; + } + else + { + msg = null; + + return false; + } + } + + protected override void CheckReinforcements(SapAttackersSpawnMessage msg) + { + if (!_attackersBySap.ContainsKey(msg.Sap.Definition)) + { + INpcPresences oreSpawn = _npcSapAttackersRepo.CreateSapAttackersSpawn(msg.Sap.Definition, msg.ZoneId); + _attackersBySap.Add(msg.Sap.Definition, oreSpawn); + } + } + + protected override bool CheckState(SapAttackersSpawnMessage msg) + { + if (msg.SapState == SapState.Closed || msg.SapState == SapState.Completed) + { + CleanupAllAttackers(msg); + + return true; + } + + return false; + } + + protected override void CleanupAllAttackers(SapAttackersSpawnMessage msg) + { + if (_attackersBySap.ContainsKey(msg.Sap.Definition)) + { + INpcPresence[] activeWaves = _attackersBySap[msg.Sap.Definition].GetAllActivePresences(); + foreach (INpcPresence wave in activeWaves) + { + ExpireWave(wave); + } + + _attackersBySap.Remove(msg.Sap.Definition); + } + } + + protected override Position FindSpawnPosition(SapAttackersSpawnMessage msg, int maxRange) + { + Position fieldCenter = msg.Sap.CurrentPosition; + RandomWalkableOnCircle finder = new RandomWalkableOnCircle(_zone, fieldCenter, maxRange, MIN_SPAWN_DIST_TOLERANCE); + + return finder.Find(out Position result) ? result : Position.Empty; + } + + protected override Position GetHomePos(SapAttackersSpawnMessage msg, Position spawnPos) + { + return msg.Sap.CurrentPosition; + } + + protected override INpcPresence GetNextWave(SapAttackersSpawnMessage msg) + { + return _attackersBySap[msg.Sap.Definition].GetNextPresence(msg.Stability); + } + } +} diff --git a/src/Perpetuum/Services/EventServices/EventType.cs b/src/Perpetuum/Services/EventServices/EventType.cs index 85de191c3..d7c5fcdd9 100644 --- a/src/Perpetuum/Services/EventServices/EventType.cs +++ b/src/Perpetuum/Services/EventServices/EventType.cs @@ -12,5 +12,6 @@ public enum EventType NpcReinforce, Environmental, PortalSpawn, + NpcSapAttackers, } } diff --git a/src/Perpetuum/Units/Unit.cs b/src/Perpetuum/Units/Unit.cs index 6145b5c25..df34a09d3 100644 --- a/src/Perpetuum/Units/Unit.cs +++ b/src/Perpetuum/Units/Unit.cs @@ -19,6 +19,7 @@ using Perpetuum.Zones.Effects; using Perpetuum.Zones.Eggs; using Perpetuum.Zones.Gates; +using Perpetuum.Zones.Intrusion; using Perpetuum.Zones.Locking; using Perpetuum.Zones.NpcSystem; using Perpetuum.Zones.PBS; @@ -925,6 +926,11 @@ internal virtual bool IsHostile(MobileTeleport teleport) return false; } + internal virtual bool IsHostile(SAP sap) + { + return false; + } + public void StopMoving() { CurrentSpeed = 0; diff --git a/src/Perpetuum/Zones/Intrusion/DestructionSAP.cs b/src/Perpetuum/Zones/Intrusion/DestructionSAP.cs index 12328834e..ddbcc50f3 100644 --- a/src/Perpetuum/Zones/Intrusion/DestructionSAP.cs +++ b/src/Perpetuum/Zones/Intrusion/DestructionSAP.cs @@ -1,6 +1,7 @@ using Perpetuum.ExportedTypes; using Perpetuum.Units; using Perpetuum.Zones.DamageProcessors; +using Perpetuum.Zones.NpcSystem; using System.Linq; namespace Perpetuum.Zones.Intrusion @@ -18,6 +19,13 @@ protected override void OnDamageTaken(Unit source, DamageTakenEventArgs e) { base.OnDamageTaken(source, e); + if (source is Npc) + { + IncrementNpcScore((int)e.TotalDamage); + + return; + } + Players.Player player = Zone.ToPlayerOrGetOwnerPlayer(source); if (player == null) { diff --git a/src/Perpetuum/Zones/Intrusion/Outpost.cs b/src/Perpetuum/Zones/Intrusion/Outpost.cs index 93b4a04b7..643f7f79b 100644 --- a/src/Perpetuum/Zones/Intrusion/Outpost.cs +++ b/src/Perpetuum/Zones/Intrusion/Outpost.cs @@ -35,6 +35,8 @@ public class Outpost : DockingBase private const int PRODUCTION_BONUS_THRESHOLD = 100; private const int MinAnnouncementDelay = 10; private const int MaxAnnouncementDelay = 30; + private const int TimeUntilCultistsAttack = 1; + private const long NianiCorporationEid = 666; private TimeRange _intrusionWaitTime => IntrusionWaitTime; private readonly IEntityServices _entityServices; @@ -43,6 +45,9 @@ public class Outpost : DockingBase private static readonly ILookup _sapInfos; private readonly EventListenerService _eventChannel; private readonly OutpostDecay _decay; + private IntervalTimer _cultistsAttackTimer = null; + + private SAP CurrentSap = null; public static StabilityBonusThreshold[] StabilityBonusThresholds { get; private set; } public static int DefenseNodesStabilityLimit { get; private set; } @@ -164,6 +169,16 @@ protected override void OnUpdate(TimeSpan time) base.OnUpdate(time); _decay.OnUpdate(time); + if (CurrentSap != null && _cultistsAttackTimer != null) + { + _cultistsAttackTimer.Update(time); + if (_cultistsAttackTimer.Passed) + { + _cultistsAttackTimer = null; + _eventChannel.PublishMessage(new SapAttackersSpawnMessage(CurrentSap, SapState.Opened, Zone.Id, GetIntrusionSiteStability())); + } + } + if (!Enabled || IntrusionInProgress) { return; @@ -284,6 +299,8 @@ private void DeploySAP() sap.TakeOver += OnSAPTakeOver; sap.TimeOut += OnSAPTimeOut; sap.AddToZone(Zone, sapInfo.Position); + CurrentSap = sap; + _cultistsAttackTimer = new IntervalTimer(TimeSpan.FromMinutes(TimeUntilCultistsAttack)); const string insertCmd = "insert into intrusionsapdeploylog (siteeid,sapdefinition) values (@siteEid,@sapDefinition)"; Db.Query().CommandText(insertCmd).SetParameter("@siteEid", Eid).SetParameter("@sapDefinition", sap.Definition).ExecuteNonQuery(); @@ -372,6 +389,8 @@ private void OnSAPTakeOver(SAP sap) TimeSpan randomDelay = FastRandom.NextTimeSpan(TimeSpan.FromMinutes(MinAnnouncementDelay), TimeSpan.FromMinutes(MaxAnnouncementDelay)); DateTime timeStamp = DateTime.UtcNow; + _eventChannel.PublishMessage(new SapAttackersSpawnMessage(CurrentSap, SapState.Completed, Zone.Id, GetIntrusionSiteStability())); + _ = Task.Delay(randomDelay).ContinueWith((t) => { PublishMessage(new SapStateMessage(Eid, Name, SapState.Completed, DateTime.UtcNow)); @@ -566,6 +585,9 @@ private void OnSAPTimeOut(SAP sap) TimeSpan randomDelay = FastRandom.NextTimeSpan(TimeSpan.FromMinutes(MinAnnouncementDelay), TimeSpan.FromMinutes(MaxAnnouncementDelay)); DateTime timeStamp = DateTime.UtcNow; + _eventChannel.PublishMessage(new SapAttackersSpawnMessage(CurrentSap, SapState.Closed, Zone.Id, GetIntrusionSiteStability())); + CurrentSap = null; + _ = Task.Delay(randomDelay).ContinueWith((t) => { PublishMessage(new SapStateMessage(Eid, Name, SapState.Closed, DateTime.UtcNow)); diff --git a/src/Perpetuum/Zones/Intrusion/SAP.cs b/src/Perpetuum/Zones/Intrusion/SAP.cs index 3d073d53f..640e7a9d3 100644 --- a/src/Perpetuum/Zones/Intrusion/SAP.cs +++ b/src/Perpetuum/Zones/Intrusion/SAP.cs @@ -1,4 +1,5 @@ using Perpetuum.Accounting.Characters; +using Perpetuum.EntityFramework; using Perpetuum.ExportedTypes; using Perpetuum.Groups.Corporations; using Perpetuum.Log; @@ -29,6 +30,12 @@ public SAPPlayerInfo(Player player) corporationEid = player.CorporationEid; } + public SAPPlayerInfo(Character character, long corporationEid) + { + this.character = character; + this.corporationEid = corporationEid; + } + public void IncrementScore(int value) { Interlocked.Add(ref score, value); @@ -65,9 +72,14 @@ public IntrusionCorporationScore(long corporationEid, int score) private static readonly TimeSpan _broadcastInfoInterval = TimeSpan.FromSeconds(2); + private readonly Character _npcAttackerCharacter; + private const int BROADCAST_INFO_TOP_SCORE_COUNT = 10; private const int BROADCAST_INFO_RANGE = 100; + private const string NianiCultistNick = "Niani Cultist"; + private const long NianiCorpEid = 666; + private readonly BeamType _enterBeamType; private readonly BeamType _exitBeamType; @@ -84,6 +96,7 @@ protected SAP(BeamType enterBeamType, BeamType exitBeamType) { _enterBeamType = enterBeamType; _exitBeamType = exitBeamType; + _npcAttackerCharacter = Character.GetByNick(NianiCultistNick); } public void AddToZone(IZone zone, Position spawnPosition) @@ -195,6 +208,21 @@ protected void IncrementPlayerScore(Player player, int score) Logger.Info($"Intrusion SAP score updated. sap = {Eid} ({ED.Name}) player = {info.character.Id} score = {info.score}"); } + protected void IncrementNpcScore(int score) + { + SAPPlayerInfo info = ImmutableInterlocked.GetOrAdd(ref _playerInfos, _npcAttackerCharacter, c => new SAPPlayerInfo(_npcAttackerCharacter, NianiCorpEid)); + info.IncrementScore(score); + + if (MaxScore > 0 && info.score >= MaxScore) + { + OnTakeOver(); + } + + ExtendTimerOnce(); + + Logger.Info($"Intrusion SAP score updated. sap = {Eid} ({ED.Name}) player = {info.character.Id} score = {info.score}"); + } + private bool _firstSapAttempt = true; private void ExtendTimerOnce() { @@ -363,14 +391,30 @@ public StabilityAffectingEvent ToStabilityAffectingEvent() { players.Add(player.character.GetPlayerRobotFromZone()); } + + long winnerCorporationEid = GetWinnerCorporationEid(); StabilityAffectingEvent.StabilityAffectBuilder builder = StabilityAffectingEvent.Builder() .WithOutpost(Site) .WithSapDefinition(Definition) .WithSapEntityID(Eid) .WithPoints(StabilityChange) .AddParticipants(players) - .WithWinnerCorp(GetWinnerCorporationEid()); + .WithWinnerCorp(winnerCorporationEid); + return builder.Build(); } + + protected override bool IsHostileFor(Unit unit) + { + return unit.ED.Options.Faction == NpcSystem.Faction.Cultist; + } + + public override void AcceptVisitor(IEntityVisitor visitor) + { + if (!TryAcceptVisitor(this, visitor)) + { + base.AcceptVisitor(visitor); + } + } } } \ No newline at end of file diff --git a/src/Perpetuum/Zones/NpcSystem/AI/BodyPullThreatHelper.cs b/src/Perpetuum/Zones/NpcSystem/AI/BodyPullThreatHelper.cs index 2751d5a11..0c0e82776 100644 --- a/src/Perpetuum/Zones/NpcSystem/AI/BodyPullThreatHelper.cs +++ b/src/Perpetuum/Zones/NpcSystem/AI/BodyPullThreatHelper.cs @@ -5,6 +5,7 @@ using Perpetuum.Services.RiftSystem; using Perpetuum.Units; using Perpetuum.Zones.Eggs; +using Perpetuum.Zones.Intrusion; using Perpetuum.Zones.NpcSystem.AI.Behaviors; using Perpetuum.Zones.NpcSystem.ThreatManaging; using Perpetuum.Zones.RemoteControl; @@ -20,7 +21,8 @@ public class BodyPullThreatHelper : IEntityVisitor, IEntityVisitor, IEntityVisitor, - IEntityVisitor + IEntityVisitor, + IEntityVisitor { private readonly SmartCreature smartCreature; @@ -140,6 +142,11 @@ public void Visit(SupportDrone supportDrone) ProcessNpcThreats(supportDrone); } + public void Visit(SAP entity) + { + ProcessNpcThreats(entity); + } + private void ProcessNpcThreats(Unit unit) { if (smartCreature.Behavior.Type != BehaviorType.RemoteControlledTurret && diff --git a/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/GuardCombatDroneAI.cs b/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/GuardCombatDroneAI.cs index 1f40265e2..0f5c2e1ad 100644 --- a/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/GuardCombatDroneAI.cs +++ b/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/GuardCombatDroneAI.cs @@ -40,7 +40,7 @@ public override void Update(TimeSpan time) return; } - if (drone.HasCommandBotPrimaryLock() != null) + if (drone.HasCommandBotPrimaryLock()) { ToAttackCombatDroneAI(); diff --git a/src/Perpetuum/Zones/NpcSystem/Creature.cs b/src/Perpetuum/Zones/NpcSystem/Creature.cs index 79b43ee53..0b3205897 100644 --- a/src/Perpetuum/Zones/NpcSystem/Creature.cs +++ b/src/Perpetuum/Zones/NpcSystem/Creature.cs @@ -4,6 +4,7 @@ using Perpetuum.Units; using Perpetuum.Zones.Effects; using Perpetuum.Zones.Eggs; +using Perpetuum.Zones.Intrusion; using Perpetuum.Zones.LandMines; using Perpetuum.Zones.Locking; using Perpetuum.Zones.Locking.Locks; @@ -16,7 +17,7 @@ public abstract class Creature : Robot { protected override void UpdateUnitVisibility(Unit target) { - if (target is AreaBomb || target is LandMine) + if (target is AreaBomb || target is LandMine || target is SAP) { UpdateVisibility(target); } diff --git a/src/Perpetuum/Zones/NpcSystem/Faction.cs b/src/Perpetuum/Zones/NpcSystem/Faction.cs index 5c8a29e79..4b995f191 100644 --- a/src/Perpetuum/Zones/NpcSystem/Faction.cs +++ b/src/Perpetuum/Zones/NpcSystem/Faction.cs @@ -4,5 +4,6 @@ public enum Faction { Niani, Syndicate, + Cultist, } } diff --git a/src/Perpetuum/Zones/NpcSystem/INpcPresence.cs b/src/Perpetuum/Zones/NpcSystem/INpcPresence.cs new file mode 100644 index 000000000..76a9eb253 --- /dev/null +++ b/src/Perpetuum/Zones/NpcSystem/INpcPresence.cs @@ -0,0 +1,26 @@ +using Perpetuum.Zones.NpcSystem.Presences; + +namespace Perpetuum.Zones.NpcSystem +{ + public interface INpcPresence + { + DynamicPresence ActivePresence { get; } + + int PresenceId { get; } + + bool Spawned { get; } + + double Threshold { get; } + + int MinStability { get; } + + void SetActivePresence(DynamicPresence presence); + + bool IsActivePresence(Presence presence); + + /// + /// Deactivate ActivePresence + /// + void DeactivatePresence(); + } +} \ No newline at end of file diff --git a/src/Perpetuum/Zones/NpcSystem/INpcPresences.cs b/src/Perpetuum/Zones/NpcSystem/INpcPresences.cs new file mode 100644 index 000000000..5be1a35d4 --- /dev/null +++ b/src/Perpetuum/Zones/NpcSystem/INpcPresences.cs @@ -0,0 +1,17 @@ +using Perpetuum.Zones.NpcSystem.Presences; + +namespace Perpetuum.Zones.NpcSystem +{ + public interface INpcPresences + { + INpcPresence GetNextPresence(double threshold); + + INpcPresence GetNextPresence(int minStability); + + bool HasActivePresence(Presence presence); + + INpcPresence GetActivePresence(Presence presence); + + INpcPresence[] GetAllActivePresences(); + } +} diff --git a/src/Perpetuum/Zones/NpcSystem/Npc.cs b/src/Perpetuum/Zones/NpcSystem/Npc.cs index 59388d7cc..81386f7d3 100644 --- a/src/Perpetuum/Zones/NpcSystem/Npc.cs +++ b/src/Perpetuum/Zones/NpcSystem/Npc.cs @@ -7,6 +7,7 @@ using Perpetuum.Services.MissionEngine.MissionTargets; using Perpetuum.Units; using Perpetuum.Zones.Eggs; +using Perpetuum.Zones.Intrusion; using Perpetuum.Zones.LandMines; using Perpetuum.Zones.RemoteControl; using System; @@ -191,13 +192,20 @@ protected override bool IsHostileFor(Unit unit) internal override bool IsHostile(Npc npc) { - return npc.ED.Options.Faction != ED.Options.Faction; + return (npc.ED.Options.Faction == Faction.Syndicate && npc.ED.Options.Faction != Faction.Syndicate) || + (npc.ED.Options.Faction != Faction.Syndicate && npc.ED.Options.Faction == Faction.Syndicate); + } + + internal override bool IsHostile(SAP sap) + { + return ED.Options.Faction == Faction.Cultist; } protected override void UpdateUnitVisibility(Unit target) { if (target is RemoteControlledCreature || target is LandMine || + target is SAP || (target is Npc npc && npc.ED.Options.Faction != ED.Options.Faction)) { UpdateVisibility(target); diff --git a/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementWave.cs b/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementWave.cs deleted file mode 100644 index 1240a87eb..000000000 --- a/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementWave.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Perpetuum.Zones.NpcSystem.Presences; - -namespace Perpetuum.Zones.NpcSystem.Reinforcements -{ - /// - /// Represents a single npc presence and its spawned state - /// - public interface INpcReinforcementWave - { - int PresenceId { get; } - DynamicPresence ActivePresence { get; } - double Threshold { get; } - bool Spawned { get; } - - /// - /// Set the ActivePresence and mark this wave as "Spawned" - /// - /// presence that is spawned to the zone - void SetActivePresence(DynamicPresence presence); - - /// - /// Test if the provided presence is the ActivePresence of this wave - /// - /// Presence to compare - /// true if reference is equal - bool IsActivePresence(Presence presence); - - /// - /// Deactivate ActivePresence - /// - void DeactivatePresence(); - } -} diff --git a/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcements.cs b/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcements.cs deleted file mode 100644 index 2985a94db..000000000 --- a/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcements.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Perpetuum.Zones.NpcSystem.Presences; - -namespace Perpetuum.Zones.NpcSystem.Reinforcements -{ - /// - /// Ore NPC Spawn - a list of presences that spawn at particular percentages of some activity completion - /// - public interface INpcReinforcements - { - /// - /// Gets the next IOreNpcData for the provided threshold percent mined - /// - /// double of range [0.-1.] representing a percentage - /// IOreNpcData - INpcReinforcementWave GetNextPresence(double threshold); - - /// - /// Does this INpcReinforcements have the provided Presence active? - /// - /// Presence to compare - /// true if provided presence is spawned in this INpcReinforcements - bool HasActivePresence(Presence presence); - - /// - /// Get the INpcReinforcementWave of some active Presence - /// - /// Presence to compare - /// INpcReinforcementWave of spawned presence - INpcReinforcementWave GetActiveWaveOfPresence(Presence presence); - - /// - /// Get all active waves (waves with active presence) - /// - /// INpcReinforcementWaves with active presences - INpcReinforcementWave[] GetAllActiveWaves(); - } -} diff --git a/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementsRepository.cs b/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementsRepository.cs index 91c3b64dd..4c505345b 100644 --- a/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementsRepository.cs +++ b/src/Perpetuum/Zones/NpcSystem/Reinforcements/INpcReinforcementsRepository.cs @@ -7,7 +7,7 @@ namespace Perpetuum.Zones.NpcSystem.Reinforcements /// public interface INpcReinforcementsRepository { - INpcReinforcements CreateOreNPCSpawn(MaterialType materialType, int zoneId); - INpcReinforcements CreateNpcBossAddSpawn(NpcBossInfo npcBossInfo, int zoneId); + INpcPresences CreateOreNPCSpawn(MaterialType materialType, int zoneId); + INpcPresences CreateNpcBossAddSpawn(NpcBossInfo npcBossInfo, int zoneId); } } diff --git a/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementWave.cs b/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementWave.cs index 52d53557b..e34d94656 100644 --- a/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementWave.cs +++ b/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementWave.cs @@ -1,14 +1,17 @@ using Perpetuum.Zones.NpcSystem.Presences; +using System; namespace Perpetuum.Zones.NpcSystem.Reinforcements { - public class NpcReinforcementWave : INpcReinforcementWave + public class NpcReinforcementWave : INpcPresence { public int PresenceId { get; } public double Threshold { get; } public bool Spawned { get; private set; } public DynamicPresence ActivePresence { get; private set; } + public int MinStability => throw new NotImplementedException(); + public NpcReinforcementWave(int presenceID, double threshold) { PresenceId = presenceID; diff --git a/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcements.cs b/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcements.cs index 87f4e19e4..7086c7510 100644 --- a/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcements.cs +++ b/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcements.cs @@ -5,14 +5,14 @@ namespace Perpetuum.Zones.NpcSystem.Reinforcements { - public class NpcReinforcements : INpcReinforcements + public class NpcReinforcements : INpcPresences { private const double ThresholdDelta = 0.1; // Sorted array of OreNpcData by threshold - private readonly INpcReinforcementWave[] _presences; + private readonly INpcPresence[] _presences; - public NpcReinforcements(INpcReinforcementWave[] presences) + public NpcReinforcements(INpcPresence[] presences) { _presences = presences.OrderBy(s => Array.IndexOf(presences, s.Threshold)).ToArray(); } @@ -22,7 +22,7 @@ public NpcReinforcements(INpcReinforcementWave[] presences) /// /// percentage expressed as [0.0-1.0] /// INpcReinforcementWave or null - public INpcReinforcementWave GetNextPresence(double threshold) + public INpcPresence GetNextPresence(double threshold) { for (int i = _presences.Length - 1; i >= 0; i--) { @@ -39,12 +39,12 @@ public bool HasActivePresence(Presence presence) return _presences.Any(w => w.IsActivePresence(presence)); } - public INpcReinforcementWave GetActiveWaveOfPresence(Presence presence) + public INpcPresence GetActivePresence(Presence presence) { return _presences.Single(w => w.IsActivePresence(presence)); } - public INpcReinforcementWave[] GetAllActiveWaves() + public INpcPresence[] GetAllActivePresences() { return _presences.Where(w => !w.IsActivePresence(null)).ToArray(); } @@ -60,5 +60,10 @@ public override string ToString() sb.AppendLine("}"); return sb.ToString(); } + + public INpcPresence GetNextPresence(int minStability) + { + throw new NotImplementedException(); + } } } diff --git a/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementsRepository.cs b/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementsRepository.cs index 559532600..2ee6fc146 100644 --- a/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementsRepository.cs +++ b/src/Perpetuum/Zones/NpcSystem/Reinforcements/NpcReinforcementsRepository.cs @@ -9,33 +9,40 @@ public class NpcReinforcementsRepository : INpcReinforcementsRepository { private const string queryStr = "SELECT threshold, presenceId from npcreinforcements WHERE targetId=@target AND reinforcementType=@type AND (zoneId IS NULL OR zoneId=@zone);"; - public INpcReinforcements CreateOreNPCSpawn(MaterialType materialType, int zoneId) + public INpcPresences CreateOreNPCSpawn(MaterialType materialType, int zoneId) { - var records = Db.Query().CommandText(queryStr) + INpcPresence[] records = Db.Query() + .CommandText(queryStr) .SetParameter("@target", materialType) .SetParameter("@type", ReinforcementType.Minerals) .SetParameter("@zone", zoneId) .Execute() - .Select(CreateFromRecord).ToArray(); + .Select(CreateFromRecord) + .ToArray(); + return new NpcReinforcements(records); } - private static INpcReinforcementWave CreateFromRecord(IDataRecord record) + private static INpcPresence CreateFromRecord(IDataRecord record) { - var presence = record.GetValue("presenceId"); - var threshold = record.GetValue("threshold"); - var pair = new NpcReinforcementWave(presence, threshold); + int presence = record.GetValue("presenceId"); + double threshold = record.GetValue("threshold"); + NpcReinforcementWave pair = new NpcReinforcementWave(presence, threshold); + return pair; } - public INpcReinforcements CreateNpcBossAddSpawn(NpcBossInfo npcBossInfo, int zoneId) + public INpcPresences CreateNpcBossAddSpawn(NpcBossInfo npcBossInfo, int zoneId) { - var records = Db.Query().CommandText(queryStr) + INpcPresence[] records = Db.Query() + .CommandText(queryStr) .SetParameter("@target", npcBossInfo.FlockId) .SetParameter("@type", ReinforcementType.Boss) .SetParameter("@zone", zoneId) .Execute() - .Select(CreateFromRecord).ToArray(); + .Select(CreateFromRecord) + .ToArray(); + return new NpcReinforcements(records); } } diff --git a/src/Perpetuum/Zones/NpcSystem/SapAttackers/ISapAttackersRepository.cs b/src/Perpetuum/Zones/NpcSystem/SapAttackers/ISapAttackersRepository.cs new file mode 100644 index 000000000..3d711166e --- /dev/null +++ b/src/Perpetuum/Zones/NpcSystem/SapAttackers/ISapAttackersRepository.cs @@ -0,0 +1,7 @@ +namespace Perpetuum.Zones.NpcSystem.SapAttackers +{ + public interface ISapAttackersRepository + { + INpcPresences CreateSapAttackersSpawn(int sapDefinition, int zoneId); + } +} diff --git a/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackerPresence.cs b/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackerPresence.cs new file mode 100644 index 000000000..ad7862a1c --- /dev/null +++ b/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackerPresence.cs @@ -0,0 +1,42 @@ +using Perpetuum.Zones.NpcSystem.Presences; +using System; + +namespace Perpetuum.Zones.NpcSystem.SapAttackers +{ + public class SapAttackerPresence : INpcPresence + { + public int PresenceId { get; } + public int MinStability { get; } + public bool Spawned { get; private set; } + public DynamicPresence ActivePresence { get; private set; } + + public double Threshold => throw new NotImplementedException(); + + public SapAttackerPresence(int presenceID, int stability) + { + PresenceId = presenceID; + MinStability = stability; + } + + public override string ToString() + { + return $"{MinStability}:{PresenceId} Spawned? {Spawned}"; + } + + public void SetActivePresence(DynamicPresence presence) + { + ActivePresence = presence; + Spawned = true; + } + + public bool IsActivePresence(Presence presence) + { + return ReferenceEquals(ActivePresence, presence); + } + + public void DeactivatePresence() + { + ActivePresence = null; + } + } +} diff --git a/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackers.cs b/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackers.cs new file mode 100644 index 000000000..339807f58 --- /dev/null +++ b/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackers.cs @@ -0,0 +1,62 @@ +using Perpetuum.Zones.NpcSystem.Presences; +using System; +using System.Linq; +using System.Text; + +namespace Perpetuum.Zones.NpcSystem.SapAttackers +{ + public class SapAttackers : INpcPresences + { + private readonly INpcPresence[] _presences; + + public SapAttackers(INpcPresence[] presences) + { + _presences = presences.OrderBy(s => Array.IndexOf(presences, s.MinStability)).ToArray(); + } + + public INpcPresence GetNextPresence(int stability) + { + for (int i = _presences.Length - 1; i >= 0; i--) + { + if (stability - _presences[i].MinStability < stability) + { + return _presences[i].Spawned ? null : _presences[i]; + } + } + return null; + } + + public bool HasActivePresence(Presence presence) + { + return _presences.Any(w => w.IsActivePresence(presence)); + } + + public INpcPresence GetActivePresence(Presence presence) + { + return _presences.Single(w => w.IsActivePresence(presence)); + } + + public INpcPresence[] GetAllActivePresences() + { + return _presences.Where(w => !w.IsActivePresence(null)).ToArray(); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("SapAttackerSpawn {"); + for (int i = 0; i < _presences.Length; i++) + { + sb.AppendLine(_presences[i].ToString()); + } + sb.AppendLine("}"); + + return sb.ToString(); + } + + public INpcPresence GetNextPresence(double threshold) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackersRepository.cs b/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackersRepository.cs new file mode 100644 index 000000000..553145d8e --- /dev/null +++ b/src/Perpetuum/Zones/NpcSystem/SapAttackers/SapAttackersRepository.cs @@ -0,0 +1,33 @@ +using Perpetuum.Data; +using System.Data; +using System.Linq; + +namespace Perpetuum.Zones.NpcSystem.SapAttackers +{ + public class SapAttackersRepository : ISapAttackersRepository + { + private const string queryStr = "SELECT MinStability, PresenceId from SapAttackers WHERE SapDefinition=@sapDefinition AND (zoneId IS NULL OR zoneId=@zone);"; + + public INpcPresences CreateSapAttackersSpawn(int sapDefinition, int zoneId) + { + INpcPresence[] records = Db.Query() + .CommandText(queryStr) + .SetParameter("@sapDefinition", sapDefinition) + .SetParameter("@zone", zoneId) + .Execute() + .Select(CreateFromRecord) + .ToArray(); + + return new SapAttackers(records); + } + + private static INpcPresence CreateFromRecord(IDataRecord record) + { + int presence = record.GetValue("PresenceId"); + int minStability = record.GetValue("MinStability"); + SapAttackerPresence pair = new SapAttackerPresence(presence, minStability); + + return pair; + } + } +} From f9a5d1b3bdd0ee9cef7bcc34c453ddcd3b75aef0 Mon Sep 17 00:00:00 2001 From: "Dolzhukov, Viktor" Date: Mon, 26 May 2025 20:29:33 +0500 Subject: [PATCH 2/2] Discord Integration (cherry picked from commit de7587a1a275d0c3c7f963d46f95391ecec2891d) --- src/Perpetuum.AdminTool/App.config | 30 ++++++- .../Perpetuum.AdminTool.csproj | 7 +- .../Properties/Resources.Designer.cs | 44 +++++----- .../Properties/Settings.Designer.cs | 22 +++-- src/Perpetuum.AdminTool/packages.config | 2 +- .../Perpetuum.Bootstrapper.csproj | 24 ++++-- .../PerpetuumBootstrapper.cs | 4 +- src/Perpetuum.Bootstrapper/app.config | 31 +++++++ src/Perpetuum.Bootstrapper/packages.config | 9 ++- .../Perpetuum.ExportedTypes.csproj | 2 +- .../Perpetuum.RequestHandlers.csproj | 19 ++++- src/Perpetuum.RequestHandlers/app.config | 23 ++++++ src/Perpetuum.RequestHandlers/packages.config | 6 +- src/Perpetuum.Server/Perpetuum.Server.csproj | 2 +- src/Perpetuum.Server/app.config | 31 ++++++- src/Perpetuum.ServerService/App.config | 38 +++++++-- .../Perpetuum.ServerService.csproj | 3 +- .../Properties/Resources.Designer.cs | 2 +- .../Properties/Settings.Designer.cs | 2 +- src/Perpetuum/GlobalConfiguration.cs | 8 ++ src/Perpetuum/Perpetuum.csproj | 66 ++++++++++++--- src/Perpetuum/Robots/OverheatHandler.cs | 2 +- .../Services/Channels/ChannelManager.cs | 57 ++++++++++++- .../EventServices/EventListenerService.cs | 81 ++++++++++++++++--- .../DiscordIntegrationMessage.cs | 17 ++++ .../DiscordIntegrationHandler.cs | 34 ++++++++ .../Services/EventServices/EventType.cs | 1 + .../Threading/Process/ProcessManager.cs | 24 +++--- .../AI/CombatDrones/CombatDroneAI.cs | 3 +- src/Perpetuum/app.config | 23 ++++++ src/Perpetuum/packages.config | 28 +++++-- 31 files changed, 532 insertions(+), 113 deletions(-) create mode 100644 src/Perpetuum.Bootstrapper/app.config create mode 100644 src/Perpetuum.RequestHandlers/app.config create mode 100644 src/Perpetuum/Services/EventServices/EventMessages/DiscordIntegrationMessage.cs create mode 100644 src/Perpetuum/Services/EventServices/EventProcessors/DiscordIntegrationHandler.cs create mode 100644 src/Perpetuum/app.config diff --git a/src/Perpetuum.AdminTool/App.config b/src/Perpetuum.AdminTool/App.config index 731f6de6c..ea62b9603 100644 --- a/src/Perpetuum.AdminTool/App.config +++ b/src/Perpetuum.AdminTool/App.config @@ -1,6 +1,30 @@ - + - + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Perpetuum.AdminTool/Perpetuum.AdminTool.csproj b/src/Perpetuum.AdminTool/Perpetuum.AdminTool.csproj index 7ebcfe590..03a4c51c6 100644 --- a/src/Perpetuum.AdminTool/Perpetuum.AdminTool.csproj +++ b/src/Perpetuum.AdminTool/Perpetuum.AdminTool.csproj @@ -8,11 +8,12 @@ WinExe Perpetuum.AdminTool Perpetuum.AdminTool - v4.6.1 + v4.6.2 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 true + x64 @@ -46,8 +47,8 @@ ..\..\packages\Autofac.4.6.1\lib\net45\Autofac.dll - - ..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll diff --git a/src/Perpetuum.AdminTool/Properties/Resources.Designer.cs b/src/Perpetuum.AdminTool/Properties/Resources.Designer.cs index 462013854..488c14e7b 100644 --- a/src/Perpetuum.AdminTool/Properties/Resources.Designer.cs +++ b/src/Perpetuum.AdminTool/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace Perpetuum.AdminTool.Properties -{ - - +namespace Perpetuum.AdminTool.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace Perpetuum.AdminTool.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Perpetuum.AdminTool.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/src/Perpetuum.AdminTool/Properties/Settings.Designer.cs b/src/Perpetuum.AdminTool/Properties/Settings.Designer.cs index 65ecd68a2..0c6287134 100644 --- a/src/Perpetuum.AdminTool/Properties/Settings.Designer.cs +++ b/src/Perpetuum.AdminTool/Properties/Settings.Designer.cs @@ -8,21 +8,17 @@ // //------------------------------------------------------------------------------ -namespace Perpetuum.AdminTool.Properties -{ - - +namespace Perpetuum.AdminTool.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/src/Perpetuum.AdminTool/packages.config b/src/Perpetuum.AdminTool/packages.config index 4e5036580..bb31bf4e9 100644 --- a/src/Perpetuum.AdminTool/packages.config +++ b/src/Perpetuum.AdminTool/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Perpetuum.Bootstrapper/Perpetuum.Bootstrapper.csproj b/src/Perpetuum.Bootstrapper/Perpetuum.Bootstrapper.csproj index 73e0e121d..df756e342 100644 --- a/src/Perpetuum.Bootstrapper/Perpetuum.Bootstrapper.csproj +++ b/src/Perpetuum.Bootstrapper/Perpetuum.Bootstrapper.csproj @@ -9,7 +9,7 @@ Properties Perpetuum.Bootstrapper Perpetuum.Bootstrapper - v4.6.1 + v4.6.2 512 @@ -36,23 +36,32 @@ ..\..\packages\Autofac.4.6.1\lib\net45\Autofac.dll - - ..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll ..\..\packages\Open.NAT.2.1.0.0\lib\net45\Open.Nat.dll - - ..\..\packages\System.Collections.Immutable.1.4.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\..\packages\System.Collections.Immutable.9.0.5\lib\netstandard2.0\System.Collections.Immutable.dll + + ..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + - - ..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + @@ -99,6 +108,7 @@ + diff --git a/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs b/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs index 97d8b919a..eb714896a 100644 --- a/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs +++ b/src/Perpetuum.Bootstrapper/PerpetuumBootstrapper.cs @@ -440,7 +440,6 @@ private void InitContainer(string gameRoot) _ = _builder.RegisterType(); _ = _builder.RegisterType().As(); - _ = _builder.Register(x => { GlobalConfiguration cfg = x.Resolve(); @@ -557,6 +556,8 @@ private void InitRelayManager() _ = _builder.RegisterType().As>(); _ = _builder.RegisterType().As>(); _ = _builder.RegisterType().As>(); + _ = _builder.RegisterType(); + _ = _builder.RegisterType().SingleInstance().OnActivated(e => { e.Context.Resolve().AddProcess(e.Instance.ToAsync().AsTimed(TimeSpan.FromSeconds(0.75))); @@ -566,6 +567,7 @@ private void InitRelayManager() e.Instance.AttachListener(e.Context.Resolve()); e.Instance.AttachListener(e.Context.Resolve()); e.Instance.AttachListener(e.Context.Resolve()); + e.Instance.AttachListener(e.Context.Resolve()); GameTimeObserver obs = new GameTimeObserver(e.Instance); obs.Subscribe(e.Context.Resolve()); }); diff --git a/src/Perpetuum.Bootstrapper/app.config b/src/Perpetuum.Bootstrapper/app.config new file mode 100644 index 000000000..99e9a32a1 --- /dev/null +++ b/src/Perpetuum.Bootstrapper/app.config @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Perpetuum.Bootstrapper/packages.config b/src/Perpetuum.Bootstrapper/packages.config index f6af12342..cce78c9e4 100644 --- a/src/Perpetuum.Bootstrapper/packages.config +++ b/src/Perpetuum.Bootstrapper/packages.config @@ -1,8 +1,11 @@  - + - - + + + + + \ No newline at end of file diff --git a/src/Perpetuum.ExportedTypes/Perpetuum.ExportedTypes.csproj b/src/Perpetuum.ExportedTypes/Perpetuum.ExportedTypes.csproj index 89ba0019c..dc3da0705 100644 --- a/src/Perpetuum.ExportedTypes/Perpetuum.ExportedTypes.csproj +++ b/src/Perpetuum.ExportedTypes/Perpetuum.ExportedTypes.csproj @@ -9,7 +9,7 @@ Properties Perpetuum.ExportedTypes Perpetuum.ExportedTypes - v4.6.1 + v4.6.2 512 diff --git a/src/Perpetuum.RequestHandlers/Perpetuum.RequestHandlers.csproj b/src/Perpetuum.RequestHandlers/Perpetuum.RequestHandlers.csproj index a0a729793..0613273b6 100644 --- a/src/Perpetuum.RequestHandlers/Perpetuum.RequestHandlers.csproj +++ b/src/Perpetuum.RequestHandlers/Perpetuum.RequestHandlers.csproj @@ -9,7 +9,7 @@ Properties Perpetuum.RequestHandlers Perpetuum.RequestHandlers - v4.6.1 + v4.6.2 512 @@ -34,13 +34,25 @@ - - ..\..\packages\System.Collections.Immutable.1.4.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\..\packages\System.Collections.Immutable.9.0.5\lib\netstandard2.0\System.Collections.Immutable.dll + + ..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + + + ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + @@ -644,6 +656,7 @@ + diff --git a/src/Perpetuum.RequestHandlers/app.config b/src/Perpetuum.RequestHandlers/app.config new file mode 100644 index 000000000..a2fe38b09 --- /dev/null +++ b/src/Perpetuum.RequestHandlers/app.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Perpetuum.RequestHandlers/packages.config b/src/Perpetuum.RequestHandlers/packages.config index d205d2758..e5b218db4 100644 --- a/src/Perpetuum.RequestHandlers/packages.config +++ b/src/Perpetuum.RequestHandlers/packages.config @@ -1,4 +1,8 @@  - + + + + + \ No newline at end of file diff --git a/src/Perpetuum.Server/Perpetuum.Server.csproj b/src/Perpetuum.Server/Perpetuum.Server.csproj index b4b385ded..d054fcbf8 100644 --- a/src/Perpetuum.Server/Perpetuum.Server.csproj +++ b/src/Perpetuum.Server/Perpetuum.Server.csproj @@ -10,7 +10,7 @@ Properties Perpetuum.Server Perpetuum.Server - v4.6.1 + v4.6.2 512 diff --git a/src/Perpetuum.Server/app.config b/src/Perpetuum.Server/app.config index 3dbff35f4..54f2f327e 100644 --- a/src/Perpetuum.Server/app.config +++ b/src/Perpetuum.Server/app.config @@ -1,3 +1,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Perpetuum.ServerService/App.config b/src/Perpetuum.ServerService/App.config index 070227f63..2123b5bba 100644 --- a/src/Perpetuum.ServerService/App.config +++ b/src/Perpetuum.ServerService/App.config @@ -1,12 +1,12 @@ - + - -
+ +
- + @@ -15,4 +15,32 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Perpetuum.ServerService/Perpetuum.ServerService.csproj b/src/Perpetuum.ServerService/Perpetuum.ServerService.csproj index a226ff8d8..60451e3fb 100644 --- a/src/Perpetuum.ServerService/Perpetuum.ServerService.csproj +++ b/src/Perpetuum.ServerService/Perpetuum.ServerService.csproj @@ -8,7 +8,7 @@ WinExe Perpetuum.ServerService Perpetuum.ServerService - v4.6.1 + v4.6.2 512 true publish\ @@ -26,6 +26,7 @@ false false true + x64 diff --git a/src/Perpetuum.ServerService/Properties/Resources.Designer.cs b/src/Perpetuum.ServerService/Properties/Resources.Designer.cs index 58f9c9d07..165b8249e 100644 --- a/src/Perpetuum.ServerService/Properties/Resources.Designer.cs +++ b/src/Perpetuum.ServerService/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Perpetuum.ServerService.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { diff --git a/src/Perpetuum.ServerService/Properties/Settings.Designer.cs b/src/Perpetuum.ServerService/Properties/Settings.Designer.cs index 927ab6498..54b2474e9 100644 --- a/src/Perpetuum.ServerService/Properties/Settings.Designer.cs +++ b/src/Perpetuum.ServerService/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace Perpetuum.ServerService.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.11.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/src/Perpetuum/GlobalConfiguration.cs b/src/Perpetuum/GlobalConfiguration.cs index 0689cebcb..9fd5157a7 100644 --- a/src/Perpetuum/GlobalConfiguration.cs +++ b/src/Perpetuum/GlobalConfiguration.cs @@ -42,5 +42,13 @@ public class GlobalConfiguration // Default camouflage bonus value. [DefaultValue(5), JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] public int CamouflageBonus { get; set; } + + public string WebHookId { get; set; } + + public string WebHookOAuth { get; set; } + + public string DiscordBotToken { get; set; } + + public string OpHelpChannelId { get; set; } } } diff --git a/src/Perpetuum/Perpetuum.csproj b/src/Perpetuum/Perpetuum.csproj index d9e75ae0d..9d0721ed1 100644 --- a/src/Perpetuum/Perpetuum.csproj +++ b/src/Perpetuum/Perpetuum.csproj @@ -20,7 +20,7 @@ 3.5 - v4.6.1 + v4.6.2 false publish\ @@ -70,25 +70,70 @@ x64 - - ..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Discord.Net.Commands.3.17.4\lib\net461\Discord.Net.Commands.dll + + + ..\..\packages\Discord.Net.Core.3.17.4\lib\net461\Discord.Net.Core.dll + + + ..\..\packages\Discord.Net.Interactions.3.17.4\lib\net461\Discord.Net.Interactions.dll + + + ..\..\packages\Discord.Net.Rest.3.17.4\lib\net461\Discord.Net.Rest.dll + + + ..\..\packages\Discord.Net.Webhook.3.17.4\lib\netstandard2.0\Discord.Net.Webhook.dll + + + ..\..\packages\Discord.Net.WebSocket.3.17.4\lib\net461\Discord.Net.WebSocket.dll + + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.8.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll - - ..\..\packages\System.Collections.Immutable.1.4.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\..\packages\System.Collections.Immutable.9.0.5\lib\netstandard2.0\System.Collections.Immutable.dll + + ..\..\packages\System.Interactive.Async.6.0.1\lib\netstandard2.0\System.Interactive.Async.dll + + + ..\..\packages\System.Linq.Async.6.0.1\lib\netstandard2.0\System.Linq.Async.dll + + + ..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + - - ..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\packages\System.Reactive.6.0.1\lib\netstandard2.0\System.Reactive.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + - - ..\..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll + + ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll @@ -345,6 +390,7 @@ + @@ -352,6 +398,7 @@ + @@ -1417,6 +1464,7 @@ + diff --git a/src/Perpetuum/Robots/OverheatHandler.cs b/src/Perpetuum/Robots/OverheatHandler.cs index b75e692a2..0f67237d3 100644 --- a/src/Perpetuum/Robots/OverheatHandler.cs +++ b/src/Perpetuum/Robots/OverheatHandler.cs @@ -29,7 +29,7 @@ public OverheatHandler(Robot robot) private double oldOverheatValue; private readonly object @lock = new object(); - public event EffectEventHandler EffectChanged; + //public event EffectEventHandler EffectChanged; public void Increase(double value = 1) { diff --git a/src/Perpetuum/Services/Channels/ChannelManager.cs b/src/Perpetuum/Services/Channels/ChannelManager.cs index bc04d602f..a20124a46 100644 --- a/src/Perpetuum/Services/Channels/ChannelManager.cs +++ b/src/Perpetuum/Services/Channels/ChannelManager.cs @@ -1,4 +1,5 @@ -using Perpetuum.Accounting.Characters; +using Newtonsoft.Json; +using Perpetuum.Accounting.Characters; using Perpetuum.Common.Loggers; using Perpetuum.Host.Requests; using Perpetuum.Services.Channels.ChatCommands; @@ -7,12 +8,17 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Text; using System.Threading; +using System.Threading.Tasks; namespace Perpetuum.Services.Channels { public class ChannelManager : IChannelManager { + private const string HelpChat = "regchannel_help"; + private readonly ISessionManager _sessionManager; private readonly IChannelRepository _channelRepository; private readonly IChannelMemberRepository _memberRepository; @@ -20,8 +26,16 @@ public class ChannelManager : IChannelManager private readonly ChannelLoggerFactory _channelLoggerFactory; private readonly ConcurrentDictionary _channels = new ConcurrentDictionary(); private readonly AdminCommandRouter _adminCommand; - - public ChannelManager(ISessionManager sessionManager, IChannelRepository channelRepository, IChannelMemberRepository memberRepository, IChannelBanRepository banRepository, ChannelLoggerFactory channelLoggerFactory, AdminCommandRouter adminCommand) + private readonly GlobalConfiguration _globalConfiguration; + + public ChannelManager( + ISessionManager sessionManager, + IChannelRepository channelRepository, + IChannelMemberRepository memberRepository, + IChannelBanRepository banRepository, + ChannelLoggerFactory channelLoggerFactory, + AdminCommandRouter adminCommand, + GlobalConfiguration globalConfiguration) { _sessionManager = sessionManager; _sessionManager.SessionAdded += OnSessionAdded; @@ -36,6 +50,8 @@ public ChannelManager(ISessionManager sessionManager, IChannelRepository channel { _channels[channel.Name] = channel; } + + _globalConfiguration = globalConfiguration; } private void OnSessionAdded(ISession session) @@ -296,8 +312,35 @@ public void Talk(string channelName, Character sender, string message, IRequest else { channel.SendMessageToAll(_sessionManager, sender, message); - } + if (channel.Name == HelpChat) + { + // Sending message to discord + + string webhookId = _globalConfiguration.WebHookId; + string webhookOAuth = _globalConfiguration.WebHookOAuth; + + if (string.IsNullOrEmpty(webhookId) || string.IsNullOrEmpty(webhookOAuth)) + { + return; + } + + string url = $"https://discord.com/api/webhooks/{webhookId}/{webhookOAuth}"; + HttpClient httpClient = new HttpClient(); + DiscordPayload payload = new DiscordPayload + { + content = $"**<{sender.Nick}>**: {message}", + }; + + string json = JsonConvert.SerializeObject(payload); + StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); + + Task.Run(async () => + { + HttpResponseMessage response = await httpClient.PostAsync(url, content); + }); + } + } } public void Announcement(string channelName, Character sender, string message) @@ -419,4 +462,10 @@ public IEnumerable GetAllChannels() return _channels.Values; } } + + internal class DiscordPayload + { + public DateTime Timestamp { get; set; } + public string content { get; set; } + } } \ No newline at end of file diff --git a/src/Perpetuum/Services/EventServices/EventListenerService.cs b/src/Perpetuum/Services/EventServices/EventListenerService.cs index 7c13617fb..a6e196fe2 100644 --- a/src/Perpetuum/Services/EventServices/EventListenerService.cs +++ b/src/Perpetuum/Services/EventServices/EventListenerService.cs @@ -1,10 +1,13 @@ -using Perpetuum.Threading.Process; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using Discord; +using Discord.WebSocket; using Perpetuum.Services.EventServices.EventMessages; using Perpetuum.Services.EventServices.EventProcessors; +using Perpetuum.Threading.Process; using Perpetuum.Timers; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Perpetuum.Services.EventServices { @@ -17,10 +20,19 @@ public class EventListenerService : Process private readonly IDictionary> _observers; private readonly ConcurrentQueue _queue; - public EventListenerService() + private readonly DiscordSocketClient _client; + private readonly GlobalConfiguration _globalConfiguration; + + public EventListenerService(GlobalConfiguration globalConfiguration) { _observers = new Dictionary>(); _queue = new ConcurrentQueue(); + + _client = new DiscordSocketClient(new DiscordSocketConfig + { + GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent + }); + _globalConfiguration = globalConfiguration; } /// @@ -36,9 +48,9 @@ public void NotifyListeners(IEventMessage message) { lock (_lock) { - if(_observers.TryGetValue(message.Type, out var list)) + if (_observers.TryGetValue(message.Type, out IList list)) { - foreach (var obs in list) + foreach (IEventProcessor obs in list) { obs.OnNext(message); } @@ -67,10 +79,10 @@ public void AttachListener(IEventProcessor observer) public override void Update(TimeSpan time) { - var timer = new TimeKeeper(TimeSpan.FromSeconds(1)); + TimeKeeper timer = new TimeKeeper(TimeSpan.FromSeconds(1)); while (!_queue.IsEmpty && !timer.Expired) { - if (_queue.TryDequeue(out var message)) + if (_queue.TryDequeue(out IEventMessage message)) { NotifyListeners(message); } @@ -79,12 +91,63 @@ public override void Update(TimeSpan time) public override void Stop() { + Task.Run(async () => + { + await _client.LogoutAsync(); + await _client.StopAsync(); + }); + + _client.Log -= Log; + _client.MessageReceived -= OnMessageReceived; + base.Stop(); } public override void Start() { base.Start(); + + string discordBotToken = _globalConfiguration.DiscordBotToken; + + if (!string.IsNullOrEmpty(discordBotToken)) + { + _client.Log += Log; + _client.MessageReceived += OnMessageReceived; + + // Run the async logic in the background + Task.Run(async () => + { + await _client.LoginAsync(TokenType.Bot, discordBotToken); + await _client.StartAsync(); + }); + } + } + + private Task OnMessageReceived(SocketMessage message) + { + ulong.TryParse(_globalConfiguration.OpHelpChannelId, out ulong channelId); + + if (!message.Author.IsBot && message.Channel.Id == channelId && !string.IsNullOrEmpty(message.Content)) + { + string nick = message.Author.GlobalName; + + if (message.Author is SocketGuildUser guildUser && + !string.IsNullOrEmpty(guildUser.DisplayName)) + { + nick = guildUser.DisplayName; + } + + PublishMessage(new DiscordIntegrationMessage(nick, message.Content)); + } + + return Task.CompletedTask; + } + + private Task Log(LogMessage msg) + { + // There has to be logging logic but meh. + + return Task.CompletedTask; } } } diff --git a/src/Perpetuum/Services/EventServices/EventMessages/DiscordIntegrationMessage.cs b/src/Perpetuum/Services/EventServices/EventMessages/DiscordIntegrationMessage.cs new file mode 100644 index 000000000..10622a2be --- /dev/null +++ b/src/Perpetuum/Services/EventServices/EventMessages/DiscordIntegrationMessage.cs @@ -0,0 +1,17 @@ +namespace Perpetuum.Services.EventServices.EventMessages +{ + /// + /// Message for sending a message from Discord to the Game + /// + public class DiscordIntegrationMessage : IEventMessage + { + public EventType Type => EventType.DiscordIntegration; + public string Nick { get; private set; } + public string Message { get; private set; } + public DiscordIntegrationMessage(string nick, string message) + { + Nick = nick; + Message = message; + } + } +} diff --git a/src/Perpetuum/Services/EventServices/EventProcessors/DiscordIntegrationHandler.cs b/src/Perpetuum/Services/EventServices/EventProcessors/DiscordIntegrationHandler.cs new file mode 100644 index 000000000..24920c494 --- /dev/null +++ b/src/Perpetuum/Services/EventServices/EventProcessors/DiscordIntegrationHandler.cs @@ -0,0 +1,34 @@ +using Perpetuum.Accounting.Characters; +using Perpetuum.Services.Channels; +using Perpetuum.Services.EventServices.EventMessages; + +namespace Perpetuum.Services.EventServices.EventProcessors +{ + /// + /// Discord Integration EventProcessor + /// + public class DiscordIntegrationHandler : EventProcessor + { + private readonly IChannelManager _channelManager; + private const string SENDER_CHARACTER_NICKNAME = "Discord"; + private const string HelpChat = "regchannel_help"; + private readonly Character _announcer; + + public DiscordIntegrationHandler(IChannelManager channelManager) + { + _announcer = Character.GetByNick(SENDER_CHARACTER_NICKNAME); + _channelManager = channelManager; + } + + public override EventType Type => EventType.DiscordIntegration; + public override void HandleMessage(IEventMessage message) + { + if (message is DiscordIntegrationMessage discordMessage) + { + string chatMessage = $"{discordMessage.Nick}: {discordMessage.Message}"; + + _channelManager.Announcement(HelpChat, _announcer, chatMessage); + } + } + } +} diff --git a/src/Perpetuum/Services/EventServices/EventType.cs b/src/Perpetuum/Services/EventServices/EventType.cs index d7c5fcdd9..6c90400fd 100644 --- a/src/Perpetuum/Services/EventServices/EventType.cs +++ b/src/Perpetuum/Services/EventServices/EventType.cs @@ -13,5 +13,6 @@ public enum EventType Environmental, PortalSpawn, NpcSapAttackers, + DiscordIntegration, } } diff --git a/src/Perpetuum/Threading/Process/ProcessManager.cs b/src/Perpetuum/Threading/Process/ProcessManager.cs index 189b872b1..7f4294ec0 100644 --- a/src/Perpetuum/Threading/Process/ProcessManager.cs +++ b/src/Perpetuum/Threading/Process/ProcessManager.cs @@ -1,9 +1,9 @@ +using Perpetuum.Log; +using Perpetuum.Timers; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; -using Perpetuum.Log; -using Perpetuum.Timers; namespace Perpetuum.Threading.Process { @@ -23,7 +23,7 @@ public ProcessManager(TimeSpan updateInterval) public void AddProcess(IProcess process) { - ImmutableInterlocked.Update(ref _processes,p => p.Add(process)); + ImmutableInterlocked.Update(ref _processes, p => p.Add(process)); } public void RemoveProcess(IProcess process) @@ -37,11 +37,13 @@ public void RemoveProcess(IProcess process) public void Start() { if (_isRunning) + { return; + } _isRunning = true; - foreach (var process in _processes) + foreach (IProcess process in _processes) { process.Start(); } @@ -68,7 +70,7 @@ public void Stop() } } - foreach (var process in _processes) + foreach (IProcess process in _processes) { StopProcess(process); } @@ -88,18 +90,18 @@ private static void StopProcess(IProcess process) private void UpdateLoop() { - var last = GlobalTimer.Elapsed; - var prevSleepTime = TimeSpan.Zero; + TimeSpan last = GlobalTimer.Elapsed; + TimeSpan prevSleepTime = TimeSpan.Zero; while (_isRunning) { - var now = GlobalTimer.Elapsed; - var elapsed = now - last; + TimeSpan now = GlobalTimer.Elapsed; + TimeSpan elapsed = now - last; last = now; try { - foreach (var process in _processes) + foreach (IProcess process in _processes) { process.Update(elapsed); } @@ -121,7 +123,9 @@ private void UpdateLoop() Thread.Sleep(prevSleepTime); } else + { prevSleepTime = TimeSpan.Zero; + } } } } diff --git a/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/CombatDroneAI.cs b/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/CombatDroneAI.cs index ebf358824..1a61cb5a0 100644 --- a/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/CombatDroneAI.cs +++ b/src/Perpetuum/Zones/NpcSystem/AI/CombatDrones/CombatDroneAI.cs @@ -5,7 +5,6 @@ using Perpetuum.Units; using Perpetuum.Zones.Locking.Locks; using Perpetuum.Zones.Movements; -using Perpetuum.Zones.NpcSystem.TargettingStrategies; using Perpetuum.Zones.RemoteControl; using System; using System.Collections.Generic; @@ -26,7 +25,7 @@ public class CombatDroneAI : BaseAI private readonly IntervalTimer primarySelectTimer = new IntervalTimer(UpdateFrequency); private List moduleActivators; private TimeSpan hostilesUpdateFrequency = TimeSpan.FromMilliseconds(UpdateFrequency); - private readonly CombatPrimaryLockSelectionStrategySelector stratSelector; + //private readonly CombatPrimaryLockSelectionStrategySelector stratSelector; private Position lastTargetPosition; private PathMovement movement; private PathMovement nextMovement; diff --git a/src/Perpetuum/app.config b/src/Perpetuum/app.config new file mode 100644 index 000000000..a2fe38b09 --- /dev/null +++ b/src/Perpetuum/app.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Perpetuum/packages.config b/src/Perpetuum/packages.config index 5f43db920..bd32f549f 100644 --- a/src/Perpetuum/packages.config +++ b/src/Perpetuum/packages.config @@ -1,11 +1,27 @@  - - + + + + + + + + + + + + - + + + + + - - - + + + + + \ No newline at end of file