diff --git a/.timetracker b/.timetracker index e152d67..616278f 100644 --- a/.timetracker +++ b/.timetracker @@ -1,5 +1,5 @@ { - "total": 650000, + "total": 656662, "sessions": [ { "begin": "2026-01-09T17:26:02+01:00", @@ -1495,6 +1495,11 @@ "begin": "2026-05-24T08:36:27+02:00", "end": "2026-05-24T08:51:27+02:00", "duration": 900 + }, + { + "begin": "2026-05-24T08:51:27+02:00", + "end": "2026-05-24T10:42:30+02:00", + "duration": 6662 } ] } \ No newline at end of file diff --git a/OrekiWoofsBees.Common/DiagnosticsUtil.cs b/OrekiWoofsBees.Common/DiagnosticsUtil.cs index 2560ff9..3105ca7 100644 --- a/OrekiWoofsBees.Common/DiagnosticsUtil.cs +++ b/OrekiWoofsBees.Common/DiagnosticsUtil.cs @@ -5,7 +5,7 @@ namespace OrekiWoofsBees.Common; public static class DiagnosticsUtil { - public static void StopAndLogTime( + public static bool StopAndLogTime( this Stopwatch stopwatch, T instance, double totalSecondsThreshold, @@ -14,7 +14,11 @@ public static class DiagnosticsUtil { stopwatch.Stop(); if (stopwatch.Elapsed.TotalSeconds >= totalSecondsThreshold) + { instance.Mod?.Logger.Warning($"{typeof(T).Name}.{callerName} ({note}) took {stopwatch.Elapsed.TotalSeconds:F2}s"); + return true; + } + return false; } public static void LogTime( diff --git a/RoamingBees/RoamingBees/Behaviors/BlockEntityBehaviorRoamingBees.cs b/RoamingBees/RoamingBees/Behaviors/BlockEntityBehaviorRoamingBees.cs index 6cfebde..11d0e89 100644 --- a/RoamingBees/RoamingBees/Behaviors/BlockEntityBehaviorRoamingBees.cs +++ b/RoamingBees/RoamingBees/Behaviors/BlockEntityBehaviorRoamingBees.cs @@ -39,7 +39,12 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti private ClimateCondition? climate; private Vec3d? windVec; - private readonly Stopwatch stopwatch = new(); + private int onParticleTickStopwatchTriggerCount; + private int spawnStopwatchTriggerCount; + private readonly Stopwatch plantInfoStopwatch = new(); + private readonly Stopwatch particleStopwatch = new(); + private readonly Stopwatch serverSpawnStopwatch = new(); + private readonly Stopwatch handleSpawnStopwatch = new(); private readonly List flowerPositions = []; private readonly List cropPositions = []; private readonly List relativePlantPositions = []; @@ -47,7 +52,6 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti public const string TARGET_BEE_PARTICLE_COUNT_ATTRIBUTE = "roamingbees_targetBeeParticleCount"; public const string RADIUS_ATTRIBUTE = "roamingbees_radius"; public const string NETWORK_CHANNEL_NAME = "bee particle spawns"; - public const int SERVER_TICK_FREQUENCY_DECREASE = 50; public int ActiveBeesCount => activeBees.Count; public IEnumerable ActiveBeesPackets => activeBees.Select(x => x.SpawnPacket); @@ -85,10 +89,25 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti plantPositionRegistry?.RegisterBeehive(Blockentity.Pos, radius); var updateFrequency = 20; - if (api.Side == EnumAppSide.Server) - updateFrequency *= SERVER_TICK_FREQUENCY_DECREASE; Blockentity.RegisterGameTickListener(OnParticleTick, updateFrequency); Blockentity.RegisterGameTickListener(UpdateClimateInfo, 10000); + Blockentity.RegisterGameTickListener(DecreaseStopwatchCounts, 60000); + if (api.Side.IsServer()) + { + Blockentity.RegisterGameTickListener(OnSpawnTick, 500); + Blockentity.RegisterGameTickListener(OnUpdatePlantInfoTick, 5000); + } + } + + private void DecreaseStopwatchCounts(float dt) + { + if (spawnStopwatchTriggerCount > 10) + modSystem?.Mod.Logger.Warning($"{nameof(spawnStopwatchTriggerCount)} is {spawnStopwatchTriggerCount}"); + spawnStopwatchTriggerCount = Math.Max(0, spawnStopwatchTriggerCount - 1); + + if (onParticleTickStopwatchTriggerCount > 10) + modSystem?.Mod.Logger.Warning($"{nameof(onParticleTickStopwatchTriggerCount)} is {onParticleTickStopwatchTriggerCount}"); + onParticleTickStopwatchTriggerCount = Math.Max(0, onParticleTickStopwatchTriggerCount - 1); } private void UpdateClimateInfo(float dt) @@ -150,7 +169,7 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti public void HandleBeeParticleSpawn(BeeSpawnPacket packet, bool catchup = false) { - stopwatch.Restart(); + handleSpawnStopwatch.Restart(); if (Blockentity.Pos != packet.HivePosition) return; @@ -189,7 +208,7 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti for (var i = 0; i < totalDelta / 0.1f; i++) bee.Step(0.1f, 0.1f); // todo } - stopwatch.StopAndLogTime(this, 0.05); + handleSpawnStopwatch.StopAndLogTime(this, 0.05); } public void Clear() @@ -204,12 +223,22 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti activeBees.Clear(); } - private void OnParticleTick(float dt) + private void OnUpdatePlantInfoTick(float dt) { - if (Block is BlockSkep && !Config.Instance.EnableOnVanillaSkeps) + if (!ShouldTick()) return; - stopwatch.Restart(); + plantInfoStopwatch.Restart(); + UpdatePlantInfo(); + var note = $"global bees: {modSystem?.GlobalActiveBees}, registered hives: {modSystem?.BeeSpawnPacketDistributor?.SpawnHandlersCount}, radius: {Config.Instance.BeeRoamingRadius}"; + plantInfoStopwatch.StopAndLogTime(this, 0.1, note); + } + + private bool ShouldTick() + { + if (Block is BlockSkep && !Config.Instance.EnableOnVanillaSkeps) + return false; + var isFgc = Block?.Code?.Domain == "fromgoldencombs"; var path = Block?.Code?.Path ?? string.Empty; var isFgcLangstroth = isFgc && path.Contains("langstrothstack", StringComparison.OrdinalIgnoreCase); @@ -218,22 +247,37 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti if (isFgcLangstroth) { if (!Config.Instance.EnableOnFgcLangstroth) - return; + return false; if (!HasRequiredLangstrothBase()) - return; + return false; } if (isFgcCeramic && !Config.Instance.EnableOnFgcCeramic) + return false; + return true; + } + + private void OnParticleTick(float dt) + { + if (!ShouldTick()) return; + var isFgc = Block?.Code?.Domain == "fromgoldencombs"; + var path = Block?.Code?.Path ?? string.Empty; + var isFgcLangstroth = isFgc && path.Contains("langstrothstack", StringComparison.OrdinalIgnoreCase); + var isFgcCeramic = isFgc && path.Contains("ceramicbroodpot", StringComparison.OrdinalIgnoreCase); + + particleStopwatch.Restart(); + entityAttributeSnapshot.Clear(); Blockentity.ToTreeAttributes(entityAttributeSnapshot); var targetBeeParticleCount = GetTargetBeeParticleCountFromBlockEntity(); - if (targetBeeParticleCount <= 0) + TargetParticleCount = Math.Max(0, targetBeeParticleCount); + + if (TargetParticleCount <= 0) return; - TargetParticleCount = Math.Max(0, targetBeeParticleCount); if (Block is BlockSkep) TargetParticleCount = Config.Instance.RoamingBeesPerVanillaSkep; if (isFgcLangstroth) @@ -241,14 +285,9 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti if (isFgcCeramic) TargetParticleCount = Config.Instance.RoamingBeesPerFgcCeramic; - if (Api.Side == EnumAppSide.Server) - { - UpdatePlantInfo(); - stopwatch.LogTime(this, 0.05, "after UpdatePlantInfo"); - } - Update(dt); - stopwatch.StopAndLogTime(this, 0.05); + if (particleStopwatch.StopAndLogTime(this, 0.1, note: $"global bees: {modSystem?.GlobalActiveBees}, registered hives: {modSystem?.BeeSpawnPacketDistributor?.SpawnHandlersCount}")) + onParticleTickStopwatchTriggerCount++; } private void Update(float dt) @@ -265,7 +304,10 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti temperature = climate?.Temperature ?? 20f; // iterate from end to easily remove from list - for (int i = activeBees.Count - 1; i >= 0; i--) + var end = activeBees.Count - 1; + end -= onParticleTickStopwatchTriggerCount * 10; + + for (int i = end; i >= 0; i--) { var bee = activeBees[i]; @@ -276,10 +318,7 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti BeeVisualParticleRenderer.Spawn(clientWorld, Blockentity.Pos, bee); } if (Api.Side == EnumAppSide.Server) - { - for (var j = 0; j < SERVER_TICK_FREQUENCY_DECREASE; j++) - bee.Step(dt / SERVER_TICK_FREQUENCY_DECREASE, windSpeed); - } + bee.Step(dt, windSpeed); if (bee.ShouldBeDespawned && modSystem != null) { @@ -289,12 +328,28 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti } TimeSinceLastSpawn += dt; - if (Api.Side == EnumAppSide.Server) - TrySpawnNewBee(TargetParticleCount); + } + + private void OnSpawnTick(float dt) + { + if (!ShouldTick()) + return; + + if (climate is null || windVec is null) + return; + + serverSpawnStopwatch.Restart(); + TrySpawnNewBee(TargetParticleCount); + if (serverSpawnStopwatch.StopAndLogTime(this, 0.1)) + spawnStopwatchTriggerCount++; } private void TrySpawnNewBee(int targetParticleCount) { + int desiredParticleCount = targetParticleCount; + if (activeBees.Count >= desiredParticleCount) + return; + if (TimeSinceLastSpawn < spawn_cooldown_seconds) return; @@ -305,7 +360,7 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti var sunPos = Api.World.Calendar.GetSunPosition(new Vec3d(Blockentity.Pos.X, Blockentity.Pos.Y, Blockentity.Pos.Z), Api.World.Calendar.TotalDays); float sunAltitudeDegrees = MathF.Asin(sunPos.Y) * 180f / GameMath.PI; - if (sunPos.Y <= cfg.SunAltitudeMinDegrees) + if (sunAltitudeDegrees <= cfg.SunAltitudeMinDegrees) return; if (rainfall >= cfg.RainfallSpawnStopThreshold) @@ -318,10 +373,6 @@ public class BlockEntityBehaviorRoamingBees(BlockEntity blockEntity) : BlockEnti if (modSystem.GlobalActiveBees >= maxGlobal) return; - int desiredParticleCount = targetParticleCount; - if (activeBees.Count >= desiredParticleCount) - return; - float sunAltitudeModifier = 1 - Math.Clamp((sunAltitudeDegrees - cfg.SunAltitudeMinDegrees) / cfg.SunAltitudeRangeDegrees, 0f, 1f); float sunAltitudeCooldownPenalty = sunAltitudeModifier * cfg.MaxSunAltitudeCooldownPenalty; diff --git a/RoamingBees/RoamingBees/Particles/BeeSpawnPacketDistributor.cs b/RoamingBees/RoamingBees/Particles/BeeSpawnPacketDistributor.cs index d88c0f7..e723149 100644 --- a/RoamingBees/RoamingBees/Particles/BeeSpawnPacketDistributor.cs +++ b/RoamingBees/RoamingBees/Particles/BeeSpawnPacketDistributor.cs @@ -17,6 +17,8 @@ internal class BeeSpawnPacketDistributor(RoamingBeesModSystem modSystem, EnumApp private Dictionary spawnHandlers { get; } = []; private Dictionary catchupHandlers { get; } = []; + public int SpawnHandlersCount => spawnHandlers.Count; + public void Register(BlockPos position, object manager) { if (manager is IBeeSpawnHandler spawnHandler)