fix(server): address PR #23 review — 4 critical bugs, 3 warnings, 11 suggestions
Critical fixes: - Add PendingRecognitionWire serialization roundtrip test - Check Option return from delay.cancel() before logging - InputQueue capacity limit (1000) with drop-oldest and warning - TODO in observation.rs references ticket #450 Warning fixes: - Hot-reload guards against invalid/empty content root - Location header mismatch warning in line pool indexing - walk_yaml() depth limit (100) against symlink loops - Consecutive reload failure counter (warns after 5+) Test additions: - Negative prerequisite filtering test for monologue lines - Integration test for pending_recognitions in observer snapshot - Eavesdrop threshold ordering assertion (Careful < default) Documentation: - Playtesting expectation comments on delay constants - is_pending() scalability note for future NPC cognitive delay - ID format regex validation in line pool spec - BTreeMap vs sort() ordering clarification in loader - Multiplayer TODO in relationships.rs references D-010 - ContentSlug ticket #452 filed for entity slug resolution 394 tests, 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,10 @@ use crate::simulation::time::{SimulationTime, TickRate};
|
||||
use bevy_ecs::prelude::*;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Maximum number of inputs the queue will hold before dropping oldest.
|
||||
/// Prevents unbounded memory growth from input flooding.
|
||||
pub const INPUT_QUEUE_CAPACITY: usize = 1000;
|
||||
|
||||
/// Queue of pending player inputs, ordered by tick
|
||||
#[derive(Resource, Debug, Default)]
|
||||
pub struct InputQueue {
|
||||
@@ -24,6 +28,7 @@ impl InputQueue {
|
||||
/// Add a new input to the queue.
|
||||
/// Inputs must be pushed in tick order for deterministic processing.
|
||||
/// Panics in debug builds if tick ordering is violated.
|
||||
/// Drops oldest inputs when capacity is exceeded.
|
||||
pub fn push(&mut self, input: PlayerInput) {
|
||||
debug_assert!(
|
||||
self.queue.back().is_none_or(|last| last.tick <= input.tick),
|
||||
@@ -31,6 +36,14 @@ impl InputQueue {
|
||||
self.queue.back().map_or(0, |last| last.tick),
|
||||
input.tick,
|
||||
);
|
||||
if self.queue.len() >= INPUT_QUEUE_CAPACITY {
|
||||
let dropped = self.queue.pop_front();
|
||||
tracing::warn!(
|
||||
"InputQueue at capacity ({}), dropping oldest input (tick={})",
|
||||
INPUT_QUEUE_CAPACITY,
|
||||
dropped.map_or(0, |d| d.tick),
|
||||
);
|
||||
}
|
||||
self.queue.push_back(input);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,13 @@ pub fn update_listening_focus(
|
||||
|
||||
// Sprint stance: stationary but too high-alert to listen
|
||||
let Some(threshold) = ListeningFocus::threshold_for_stance(stance) else {
|
||||
if focus.eavesdrop_target.is_some() || focus.stationary_ticks > 0 {
|
||||
tracing::debug!(
|
||||
"Sprint stance resets eavesdrop: stationary_ticks={}, had_target={}",
|
||||
focus.stationary_ticks,
|
||||
focus.eavesdrop_target.is_some(),
|
||||
);
|
||||
}
|
||||
focus.stationary_ticks = 0;
|
||||
focus.eavesdrop_target = None;
|
||||
continue;
|
||||
@@ -426,6 +433,23 @@ mod tests {
|
||||
assert_eq!(focus.stationary_ticks, EAVESDROP_THRESHOLD);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Constant invariants
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn eavesdrop_threshold_careful_less_than_normal() {
|
||||
// T4: The careful threshold MUST be strictly less than the normal
|
||||
// threshold — careful stance rewards patience with faster eavesdrop
|
||||
// activation (D-053, D-018).
|
||||
assert!(
|
||||
EAVESDROP_THRESHOLD_CAREFUL < EAVESDROP_THRESHOLD,
|
||||
"EAVESDROP_THRESHOLD_CAREFUL ({}) must be < EAVESDROP_THRESHOLD ({})",
|
||||
EAVESDROP_THRESHOLD_CAREFUL,
|
||||
EAVESDROP_THRESHOLD,
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Edge cases
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user