Game Scene Split up
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
#if UNITY_EDITOR
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.NetCode;
|
||||
using Unity.Transforms;
|
||||
|
||||
namespace ProjectM.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// EDITOR-ONLY server receiver for <see cref="DebugCommandRequest"/> dev-tool RPCs (from the DebugOverlay or
|
||||
/// execute_code). Applies authoritative effects so the dev buttons exercise the REAL server paths and work
|
||||
/// over a live connection too: force/end sieges, grant resources/upgrades, teleport, god-mode, heal/kill,
|
||||
/// advance the goal. Sender-targeted ops resolve the player via SourceConnection -> NetworkId -> GhostOwner
|
||||
/// (the RegionTransitSystem pattern). Plain server SimulationSystemGroup (NOT the predicted loop). Reuses
|
||||
/// StorageMath / StatModifier / RegionMath + the wave/cycle singletons. The whole system is #if UNITY_EDITOR
|
||||
/// (stripped from builds); the wire TYPE (<see cref="DebugCommandRequest"/>) is unconditional so the RPC
|
||||
/// collection hash matches across peers. Non-Burst (managed-simple, editor-only) — perf is irrelevant.
|
||||
/// </summary>
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||
[UpdateInGroup(typeof(SimulationSystemGroup))]
|
||||
public partial struct DebugCommandReceiveSystem : ISystem
|
||||
{
|
||||
EntityQuery m_Husks;
|
||||
|
||||
public void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_Husks = state.GetEntityQuery(ComponentType.ReadOnly<EnemyTag>());
|
||||
var builder = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<DebugCommandRequest, ReceiveRpcCommandRequest>();
|
||||
state.RequireForUpdate(state.GetEntityQuery(builder));
|
||||
}
|
||||
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
|
||||
// Connection NetworkId -> player entity (for sender-targeted ops).
|
||||
var playerByConn = new NativeHashMap<int, Entity>(8, Allocator.Temp);
|
||||
foreach (var (owner, e) in SystemAPI.Query<RefRO<GhostOwner>>().WithAll<PlayerTag>().WithEntityAccess())
|
||||
playerByConn[owner.ValueRO.NetworkId] = e;
|
||||
|
||||
bool haveCycle = SystemAPI.TryGetSingletonEntity<CycleState>(out var cycleEntity);
|
||||
|
||||
foreach (var (request, receive, reqEntity) in
|
||||
SystemAPI.Query<RefRO<DebugCommandRequest>, RefRO<ReceiveRpcCommandRequest>>().WithEntityAccess())
|
||||
{
|
||||
var cmd = request.ValueRO;
|
||||
|
||||
Entity sender = Entity.Null;
|
||||
var connEntity = receive.ValueRO.SourceConnection;
|
||||
if (SystemAPI.HasComponent<NetworkId>(connEntity))
|
||||
playerByConn.TryGetValue(SystemAPI.GetComponent<NetworkId>(connEntity).Value, out sender);
|
||||
|
||||
switch (cmd.Op)
|
||||
{
|
||||
case DebugOp.SpawnWave:
|
||||
if (haveCycle && SystemAPI.HasComponent<ThreatState>(cycleEntity))
|
||||
{
|
||||
var ts = SystemAPI.GetComponent<ThreatState>(cycleEntity);
|
||||
ts.PendingSiegeSize = math.max(1, cmd.ArgA);
|
||||
ts.ArmTick = 0; // fire as soon as CyclePhaseSystem sees it
|
||||
SystemAPI.SetComponent(cycleEntity, ts);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.EndSiege:
|
||||
case DebugOp.SetCalm:
|
||||
CullHusks(ref ecb);
|
||||
if (SystemAPI.TryGetSingletonEntity<WaveState>(out var we))
|
||||
{
|
||||
var w = SystemAPI.GetComponent<WaveState>(we);
|
||||
w.Phase = WavePhase.Lull;
|
||||
w.RemainingToSpawn = 0;
|
||||
SystemAPI.SetComponent(we, w);
|
||||
}
|
||||
if (haveCycle && SystemAPI.HasComponent<ThreatState>(cycleEntity))
|
||||
{
|
||||
var ts = SystemAPI.GetComponent<ThreatState>(cycleEntity);
|
||||
ts.PendingSiegeSize = 0;
|
||||
ts.ArmTick = 0;
|
||||
ts.SiegeStartTick = 0;
|
||||
SystemAPI.SetComponent(cycleEntity, ts);
|
||||
}
|
||||
if (cmd.Op == DebugOp.SetCalm && haveCycle)
|
||||
{
|
||||
var cs = SystemAPI.GetComponent<CycleState>(cycleEntity);
|
||||
cs.Phase = CyclePhase.Calm;
|
||||
cs.PhaseEndTick = 0;
|
||||
SystemAPI.SetComponent(cycleEntity, cs);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.ClearEnemies:
|
||||
CullHusks(ref ecb);
|
||||
break;
|
||||
|
||||
case DebugOp.GrantResource:
|
||||
if (SystemAPI.TryGetSingletonEntity<ResourceLedger>(out var ledgerE))
|
||||
{
|
||||
var ledger = SystemAPI.GetBuffer<StorageEntry>(ledgerE);
|
||||
StorageMath.Deposit(ledger, (ushort)cmd.ArgA, cmd.ArgB);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.GrantUpgrade:
|
||||
if (sender != Entity.Null && SystemAPI.HasBuffer<StatModifier>(sender))
|
||||
GrowDamageModifier(SystemAPI.GetBuffer<StatModifier>(sender));
|
||||
break;
|
||||
|
||||
case DebugOp.Teleport:
|
||||
if (sender != Entity.Null && SystemAPI.HasComponent<RegionTag>(sender)
|
||||
&& SystemAPI.HasComponent<LocalTransform>(sender))
|
||||
{
|
||||
byte region = (byte)cmd.ArgA;
|
||||
SystemAPI.GetComponentRW<RegionTag>(sender).ValueRW.Region = region;
|
||||
float3 baseCenter = new float3(0f, 1f, 0f);
|
||||
if (SystemAPI.TryGetSingleton<BaseAnchor>(out var anchor))
|
||||
baseCenter = BaseGridMath.PlotCenter(anchor);
|
||||
SystemAPI.GetComponentRW<LocalTransform>(sender).ValueRW.Position =
|
||||
RegionMath.RegionOrigin(region, baseCenter);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.ToggleGod:
|
||||
if (sender != Entity.Null && SystemAPI.HasComponent<DebugGodMode>(sender))
|
||||
SystemAPI.SetComponentEnabled<DebugGodMode>(sender, !SystemAPI.IsComponentEnabled<DebugGodMode>(sender));
|
||||
break;
|
||||
|
||||
case DebugOp.Heal:
|
||||
if (sender != Entity.Null && SystemAPI.HasComponent<Health>(sender))
|
||||
{
|
||||
var h = SystemAPI.GetComponent<Health>(sender);
|
||||
h.Current = SystemAPI.HasComponent<EffectiveCharacterStats>(sender)
|
||||
? SystemAPI.GetComponent<EffectiveCharacterStats>(sender).MaxHealth
|
||||
: h.Max;
|
||||
SystemAPI.SetComponent(sender, h);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.KillPlayer:
|
||||
if (sender != Entity.Null && SystemAPI.HasComponent<Health>(sender))
|
||||
{
|
||||
var h = SystemAPI.GetComponent<Health>(sender);
|
||||
h.Current = 0f;
|
||||
SystemAPI.SetComponent(sender, h);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.AdvanceGoal:
|
||||
if (haveCycle && SystemAPI.HasComponent<GoalProgress>(cycleEntity))
|
||||
{
|
||||
var g = SystemAPI.GetComponent<GoalProgress>(cycleEntity);
|
||||
g.Charge += math.max(1, cmd.ArgA);
|
||||
SystemAPI.SetComponent(cycleEntity, g);
|
||||
}
|
||||
break;
|
||||
|
||||
case DebugOp.SetHeat:
|
||||
if (haveCycle && SystemAPI.HasComponent<ThreatState>(cycleEntity))
|
||||
{
|
||||
var ts = SystemAPI.GetComponent<ThreatState>(cycleEntity);
|
||||
ts.Heat = cmd.ArgA;
|
||||
SystemAPI.SetComponent(cycleEntity, ts);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ecb.DestroyEntity(reqEntity);
|
||||
}
|
||||
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
playerByConn.Dispose();
|
||||
}
|
||||
|
||||
void CullHusks(ref EntityCommandBuffer ecb)
|
||||
{
|
||||
var husks = m_Husks.ToEntityArray(Allocator.Temp);
|
||||
for (int i = 0; i < husks.Length; i++)
|
||||
ecb.DestroyEntity(husks[i]);
|
||||
husks.Dispose();
|
||||
}
|
||||
|
||||
static void GrowDamageModifier(DynamicBuffer<StatModifier> mods)
|
||||
{
|
||||
const uint debugSourceId = 0x00DEB061u; // distinct debug sentinel (replace-by-SourceId keeps it bounded)
|
||||
for (int i = 0; i < mods.Length; i++)
|
||||
{
|
||||
if (mods[i].SourceId == debugSourceId && mods[i].Target == (byte)StatTarget.Damage)
|
||||
{
|
||||
var m = mods[i];
|
||||
m.Value += 0.25f;
|
||||
mods[i] = m;
|
||||
return;
|
||||
}
|
||||
}
|
||||
mods.Add(new StatModifier
|
||||
{
|
||||
Target = (byte)StatTarget.Damage,
|
||||
Op = (byte)ModOp.PercentAdd,
|
||||
Value = 0.25f,
|
||||
SourceId = debugSourceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user