Tuning Knobs
This commit is contained in:
@@ -61,6 +61,8 @@ namespace ProjectM.Server
|
||||
float dt = SystemAPI.Time.DeltaTime;
|
||||
var serverTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
|
||||
uint now = serverTick.TickIndexForValidTick;
|
||||
// Live feel knobs (MC-0): one read, guarded at use. Server-only — clients never simulate enemies.
|
||||
var tune = SystemAPI.TryGetSingleton<TuningConfig>(out var tcfg) ? tcfg : TuningConfig.Defaults();
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
bool havePhysics = SystemAPI.TryGetSingleton<PhysicsWorldSingleton>(out var physics);
|
||||
uint envMask = SystemAPI.TryGetSingleton<WorldCollisionConfig>(out var worldCol) ? worldCol.EnvironmentMask : 0u;
|
||||
@@ -165,7 +167,7 @@ namespace ProjectM.Server
|
||||
}
|
||||
if (ready)
|
||||
{
|
||||
uint windupTicks = (uint)math.max(1, Tuning.AttackWindupTicks);
|
||||
uint windupTicks = (uint)math.max(1f, tune.GruntWindupTicks);
|
||||
windup.ValueRW.WindUpUntilTick = TickUtil.NonZero(now + windupTicks);
|
||||
}
|
||||
}
|
||||
@@ -173,10 +175,12 @@ namespace ProjectM.Server
|
||||
|
||||
// --- Charger pass: a Husk variant baked with LungeState commits to a punishable fixed-direction lunge.
|
||||
// Component-presence is the discriminator; the Grunt pass above excludes these via .WithNone<LungeState>().
|
||||
const float ChargerLungeSpeed = 16f; // units/s while lunging
|
||||
const uint ChargerLungeDurationTicks = 18; // ~0.30 s of committed travel
|
||||
const uint ChargerWindupTicks = 30; // ~0.50 s readable telegraph (>= interp delay + reaction)
|
||||
const uint ChargerWhiffStaggerTicks = 36; // ~0.60 s punish window on a whiff
|
||||
// Charger feel knobs — live-tunable via TuningConfig (MC-0), guarded at the read site. Server-only
|
||||
// (clients never simulate Chargers); the >=1-tick floor avoids a degenerate instant/no-travel lunge.
|
||||
float ChargerLungeSpeed = math.max(0f, tune.ChargerLungeSpeed); // units/s while lunging
|
||||
uint ChargerLungeDurationTicks = (uint)math.max(1f, tune.ChargerLungeDurationTicks); // committed travel
|
||||
uint ChargerWindupTicks = (uint)math.max(1f, tune.ChargerWindupTicks); // readable telegraph lead
|
||||
uint ChargerWhiffStaggerTicks = (uint)math.max(1f, tune.ChargerWhiffStaggerTicks); // punish window
|
||||
uint chargerWhiffsThisTick = 0;
|
||||
foreach (var (xform, stats, cooldown, knockback, windup, lunge) in
|
||||
SystemAPI.Query<RefRW<LocalTransform>, RefRO<EnemyStats>, RefRW<EnemyAttackCooldown>,
|
||||
|
||||
@@ -165,6 +165,13 @@ namespace ProjectM.Server
|
||||
SystemAPI.SetComponent(cycleEntity, ts);
|
||||
}
|
||||
break;
|
||||
case DebugOp.SetTuning:
|
||||
if (SystemAPI.TryGetSingleton<TuningConfig>(out var tuningCfg))
|
||||
{
|
||||
TuningConfig.Apply(ref tuningCfg, (byte)cmd.ArgA, cmd.ArgB / 1000f);
|
||||
SystemAPI.SetSingleton(tuningCfg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ecb.DestroyEntity(reqEntity);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#if UNITY_EDITOR
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// MC-0 — EDITOR-ONLY server owner of the authoritative <see cref="TuningConfig"/> singleton. Ensures+seeds it
|
||||
/// to <see cref="TuningConfig.Defaults"/> in OnCreate, then every <see cref="BroadcastPeriodTicks"/> ships the
|
||||
/// FULL config to every connection as a <see cref="DebugTuningReport"/> (so a PREDICTING client's DashSystem —
|
||||
/// including an MPPM thin client that has no overlay — converges on the tuned values; full state, not a delta,
|
||||
/// so a late-joiner converges in one report). The singleton is mutated by <c>DebugCommandReceiveSystem</c>'s
|
||||
/// SetTuning op (same plain server group). Verbatim <c>DevTelemetrySystem</c> shape. Plain server
|
||||
/// <see cref="SimulationSystemGroup"/> (NOT the predicted loop); non-Burst (managed-simple, editor-only).
|
||||
/// Stripped from builds; the wire TYPE <see cref="DebugTuningReport"/> + <see cref="TuningConfig"/> are
|
||||
/// unconditional, so in a release build no system creates the singleton and consumers fall back to Defaults().
|
||||
/// </summary>
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||
[UpdateInGroup(typeof(SimulationSystemGroup))]
|
||||
public partial struct TuningBroadcastSystem : ISystem
|
||||
{
|
||||
const uint BroadcastPeriodTicks = 15;
|
||||
|
||||
public void OnCreate(ref SystemState state)
|
||||
{
|
||||
state.RequireForUpdate<NetworkTime>();
|
||||
if (state.GetEntityQuery(ComponentType.ReadWrite<TuningConfig>()).IsEmpty)
|
||||
{
|
||||
var e = state.EntityManager.CreateEntity(typeof(TuningConfig));
|
||||
state.EntityManager.SetComponentData(e, TuningConfig.Defaults());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var serverTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
|
||||
if (!serverTick.IsValid)
|
||||
return;
|
||||
uint now = serverTick.TickIndexForValidTick;
|
||||
if (now == 0 || (now % BroadcastPeriodTicks) != 0)
|
||||
return;
|
||||
|
||||
var report = TuningConfig.ToReport(SystemAPI.GetSingleton<TuningConfig>());
|
||||
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
foreach (var (netId, connEnt) in SystemAPI.Query<RefRO<NetworkId>>().WithEntityAccess())
|
||||
{
|
||||
var req = ecb.CreateEntity();
|
||||
ecb.AddComponent(req, report);
|
||||
ecb.AddComponent(req, new SendRpcCommandRequest { TargetConnection = connEnt });
|
||||
}
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58b4be546da44eb499b7cba0c7b04ea9
|
||||
Reference in New Issue
Block a user