1use std::{
4 cmp::Reverse,
5 collections::{BinaryHeap, VecDeque},
6 hash::Hash,
7 mem,
8 sync::Arc,
9};
10
11use crate::{
12 entity::Entity,
13 player::{
14 Player, PlayerSession, PlayerSessionId,
15 connection::NetworkConnection,
16 connection::{ScheduledPacketExecution, ScheduledPlayPacket},
17 },
18};
19use parking_lot::Condvar;
20use rustc_hash::{FxHashMap, FxHashSet};
21use steel_utils::{locks::SyncMutex, translations};
22use tokio::sync::Notify;
23use tokio::task::yield_now;
24
25use super::Server;
26
27const MAX_OUTSTANDING_PACKETS_PER_PLAYER: usize = 8_192;
31const MAX_OUTSTANDING_BYTES_PER_PLAYER: usize = 32 * 1024 * 1024;
32const PACKET_ADMISSION_OVERHEAD: usize = 256;
34
35#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
36struct PlayerPacketLaneKey(PlayerSessionId);
37
38impl PlayerPacketLaneKey {
39 const fn new(session: &PlayerSession) -> Self {
40 Self(session.id())
41 }
42}
43
44#[derive(Clone, Copy)]
46pub(crate) struct PlayerPacketTransition(PacketLanePause<PlayerPacketLaneKey>);
47
48struct PendingPlayPacket {
49 session: Arc<PlayerSession>,
50 packet: ScheduledPlayPacket,
51}
52
53pub(super) struct PacketProcessor {
60 queued: PacketQueue<PlayerPacketLaneKey, PendingPlayPacket>,
61}
62
63impl PacketProcessor {
64 pub(super) fn new() -> Self {
65 Self {
66 queued: PacketQueue::new(),
67 }
68 }
69
70 pub(super) fn schedule(
71 &self,
72 player: Arc<Player>,
73 packet: ScheduledPlayPacket,
74 payload_bytes: usize,
75 ) {
76 let player_id = player.gameprofile.id();
77 let lane_key = PlayerPacketLaneKey::new(&player.session);
78 if player.connection.closed() {
79 self.queued.discard_lane(lane_key);
80 return;
81 }
82
83 let execution = packet.execution();
84 let admission_bytes = payload_bytes.saturating_add(PACKET_ADMISSION_OVERHEAD);
85 let result = self.queued.try_submit(
86 lane_key,
87 execution,
88 admission_bytes,
89 PendingPlayPacket {
90 session: Arc::clone(&player.session),
91 packet,
92 },
93 );
94 let Err(error) = result else {
95 return;
96 };
97 if error == PacketAdmissionError::Stopped {
98 return;
99 }
100
101 tracing::warn!(
102 player_id = %player_id,
103 entity_id = player.id(),
104 ?error,
105 "Disconnecting player after inbound packet admission limit"
106 );
107 player.disconnect(translations::DISCONNECT_EXCEEDED_PACKET_RATE.msg());
108 self.queued.discard_lane(lane_key);
109 }
110
111 pub(super) fn run(&self, server: &Arc<Server>) {
113 while let Some(mut work) = self.queued.next() {
114 let Some(pending) = work.take() else {
115 continue;
116 };
117 let Some(player) = pending.session.current_player() else {
118 continue;
119 };
120 if !Self::packet_is_runnable(
121 &player,
122 &pending.packet,
123 server.cancel_token.is_cancelled(),
124 ) {
125 continue;
126 }
127
128 pending.packet.handle(player, server);
129 }
130 }
131
132 pub(super) fn pause_player_session(
134 &self,
135 session: &PlayerSession,
136 ) -> Option<PlayerPacketTransition> {
137 self.queued
138 .pause_lane(PlayerPacketLaneKey::new(session))
139 .map(PlayerPacketTransition)
140 }
141
142 pub(super) fn resume_player_session(&self, transition: PlayerPacketTransition) -> bool {
144 self.queued.resume_lane(transition.0)
145 }
146
147 pub(super) fn discard_player_session(&self, session: &PlayerSession) {
149 self.queued.discard_lane(PlayerPacketLaneKey::new(session));
150 }
151
152 fn packet_is_runnable(
153 player: &Player,
154 packet: &ScheduledPlayPacket,
155 server_cancelled: bool,
156 ) -> bool {
157 !player.connection.closed()
158 && !server_cancelled
159 && player.gate_domain_switch_packet(
160 packet.is_domain_handshake_packet(),
161 packet.is_perform_respawn(),
162 )
163 }
164
165 pub(super) fn open_after_tick(&self) {
167 self.queued.open();
168 }
169
170 pub(super) async fn wait_for_overload_progress(&self) {
172 let Some(completed) = self.queued.progress_baseline() else {
173 return;
174 };
175 yield_now().await;
176 self.queued.wait_for_progress_since(completed).await;
177 }
178
179 pub(super) async fn close_for_tick(&self) {
181 self.queued.drain_for_tick().await;
182 }
183
184 pub(super) fn stop(&self) {
186 self.queued.stop();
187 }
188}
189
190impl Default for PacketProcessor {
191 fn default() -> Self {
192 Self::new()
193 }
194}
195
196#[derive(Clone, Copy, Eq, PartialEq)]
197enum PacketPhase {
198 Closed,
199 Open,
200 Draining(u64),
201 Stopped,
202}
203
204struct SequencedPacket<T> {
205 sequence: u64,
206 execution: ScheduledPacketExecution,
207 admission_bytes: usize,
208 value: T,
209}
210
211struct DeferredPacket<T> {
212 execution: ScheduledPacketExecution,
213 admission_bytes: usize,
214 value: T,
215}
216
217struct PacketLane<T> {
218 queued: VecDeque<SequencedPacket<T>>,
219 deferred: VecDeque<DeferredPacket<T>>,
220 active: bool,
221 pause_generation: Option<u64>,
222 outstanding_packets: usize,
223 outstanding_bytes: usize,
224}
225
226impl<T> PacketLane<T> {
227 const fn new() -> Self {
228 Self {
229 queued: VecDeque::new(),
230 deferred: VecDeque::new(),
231 active: false,
232 pause_generation: None,
233 outstanding_packets: 0,
234 outstanding_bytes: 0,
235 }
236 }
237}
238
239#[derive(Clone, Copy)]
240struct PacketAdmissionLimits {
241 per_player_packets: usize,
242 per_player_bytes: usize,
243}
244
245impl PacketAdmissionLimits {
246 const PRODUCTION: Self = Self {
247 per_player_packets: MAX_OUTSTANDING_PACKETS_PER_PLAYER,
248 per_player_bytes: MAX_OUTSTANDING_BYTES_PER_PLAYER,
249 };
250}
251
252#[derive(Clone, Copy, Debug, Eq, PartialEq)]
253enum PacketAdmissionError {
254 Stopped,
255 PlayerPacketLimit,
256 PlayerByteLimit,
257}
258
259struct PacketQueueState<K, T> {
260 phase: PacketPhase,
261 lanes: FxHashMap<K, PacketLane<T>>,
262 player_local_ready: BinaryHeap<Reverse<(u64, K)>>,
263 serialized_ready: BinaryHeap<Reverse<(u64, K)>>,
264 exclusive_ready: BinaryHeap<Reverse<(u64, K)>>,
265 serialized: BinaryHeap<Reverse<u64>>,
266 exclusive: BinaryHeap<Reverse<u64>>,
267 next_sequence: u64,
268 next_pause_generation: u64,
269 active: usize,
270 serialized_active: bool,
271 exclusive_active: bool,
272 completed: u64,
273}
274
275struct PacketQueue<K, T> {
277 state: SyncMutex<PacketQueueState<K, T>>,
278 limits: PacketAdmissionLimits,
279 work_available: Condvar,
280 idle: Notify,
281 progress: Notify,
282}
283
284#[derive(Clone, Copy)]
285struct PacketLanePause<K> {
286 key: K,
287 generation: u64,
288}
289
290impl<K, T> PacketQueue<K, T>
291where
292 K: Copy + Eq + Hash + Ord,
293{
294 fn new() -> Self {
295 Self::with_limits(PacketAdmissionLimits::PRODUCTION)
296 }
297
298 fn with_limits(limits: PacketAdmissionLimits) -> Self {
299 Self {
300 state: SyncMutex::new(PacketQueueState {
301 phase: PacketPhase::Closed,
302 lanes: FxHashMap::default(),
303 player_local_ready: BinaryHeap::new(),
304 serialized_ready: BinaryHeap::new(),
305 exclusive_ready: BinaryHeap::new(),
306 serialized: BinaryHeap::new(),
307 exclusive: BinaryHeap::new(),
308 next_sequence: 0,
309 next_pause_generation: 0,
310 active: 0,
311 serialized_active: false,
312 exclusive_active: false,
313 completed: 0,
314 }),
315 limits,
316 work_available: Condvar::new(),
317 idle: Notify::new(),
318 progress: Notify::new(),
319 }
320 }
321
322 fn try_submit(
323 &self,
324 key: K,
325 execution: ScheduledPacketExecution,
326 admission_bytes: usize,
327 value: T,
328 ) -> Result<(), PacketAdmissionError> {
329 let mut state = self.state.lock();
330 if state.phase == PacketPhase::Stopped {
331 return Err(PacketAdmissionError::Stopped);
332 }
333
334 let (player_packets, player_bytes) = state
335 .lanes
336 .get(&key)
337 .map_or_default(|lane| (lane.outstanding_packets, lane.outstanding_bytes));
338 if player_packets >= self.limits.per_player_packets {
339 return Err(PacketAdmissionError::PlayerPacketLimit);
340 }
341 if admission_bytes > self.limits.per_player_bytes.saturating_sub(player_bytes) {
342 return Err(PacketAdmissionError::PlayerByteLimit);
343 }
344 let paused = state
345 .lanes
346 .get(&key)
347 .is_some_and(|lane| lane.pause_generation.is_some());
348 if paused {
349 let lane = state.lanes.entry(key).or_insert_with(PacketLane::new);
350 lane.outstanding_packets += 1;
351 lane.outstanding_bytes += admission_bytes;
352 lane.deferred.push_back(DeferredPacket {
353 execution,
354 admission_bytes,
355 value,
356 });
357 return Ok(());
358 }
359
360 let sequence = state.next_sequence;
361 assert!(sequence != u64::MAX, "packet submission sequence exhausted");
362 state.next_sequence = sequence + 1;
363
364 let lane = state.lanes.entry(key).or_insert_with(PacketLane::new);
365 let became_ready = !lane.active && lane.queued.is_empty();
366 lane.outstanding_packets += 1;
367 lane.outstanding_bytes += admission_bytes;
368 lane.queued.push_back(SequencedPacket {
369 sequence,
370 execution,
371 admission_bytes,
372 value,
373 });
374 if became_ready {
375 Self::mark_ready(&mut state, sequence, key, execution);
376 }
377 Self::mark_global_order(&mut state, sequence, execution);
378 let should_wake = became_ready && state.phase == PacketPhase::Open;
379 drop(state);
380 if should_wake {
381 self.work_available.notify_one();
382 }
383 Ok(())
384 }
385
386 #[cfg(test)]
387 fn submit(&self, key: K, execution: ScheduledPacketExecution, value: T) {
388 let result = self.try_submit(key, execution, 1, value);
389 assert!(
390 matches!(result, Ok(()) | Err(PacketAdmissionError::Stopped)),
391 "test packet exceeded admission limits: {result:?}"
392 );
393 }
394
395 fn open(&self) {
396 let mut state = self.state.lock();
397 if state.phase == PacketPhase::Stopped {
398 return;
399 }
400 state.phase = PacketPhase::Open;
401 let should_wake = Self::select_next(&state, None).is_some();
402 drop(state);
403 if should_wake {
404 self.work_available.notify_all();
405 }
406 }
407
408 #[cfg(test)]
409 fn close(&self) {
410 let mut state = self.state.lock();
411 if state.phase != PacketPhase::Stopped {
412 state.phase = PacketPhase::Closed;
413 }
414 }
415
416 async fn drain_for_tick(&self) {
417 let Some((before_sequence, should_wake)) = self.begin_tick_drain() else {
418 return;
419 };
420 if should_wake {
421 self.work_available.notify_all();
422 }
423
424 loop {
425 let idle = self.idle.notified();
426 if self.tick_drain_complete(before_sequence) {
427 break;
428 }
429 idle.await;
430 }
431
432 self.finish_tick_drain(before_sequence);
433 }
434
435 fn finish_tick_drain(&self, before_sequence: u64) {
436 let mut state = self.state.lock();
437 if state.phase == PacketPhase::Draining(before_sequence) {
438 state.phase = PacketPhase::Closed;
439 }
440 }
441
442 fn begin_tick_drain(&self) -> Option<(u64, bool)> {
443 let mut state = self.state.lock();
444 let before_sequence = match state.phase {
445 PacketPhase::Stopped => None,
446 PacketPhase::Draining(before_sequence) => Some(before_sequence),
447 PacketPhase::Closed | PacketPhase::Open => {
448 let before_sequence = state.next_sequence;
449 state.phase = PacketPhase::Draining(before_sequence);
450 Some(before_sequence)
451 }
452 }?;
453 let should_wake = Self::select_next(&state, Some(before_sequence)).is_some();
454 Some((before_sequence, should_wake))
455 }
456
457 fn tick_drain_complete(&self, before_sequence: u64) -> bool {
458 let state = self.state.lock();
459 if state.phase == PacketPhase::Stopped {
460 return true;
461 }
462 if state.active != 0 {
463 return false;
464 }
465 Self::next_ready_sequence(&state).is_none_or(|sequence| sequence >= before_sequence)
466 }
467
468 async fn wait_for_progress_since(&self, completed: u64) {
469 loop {
470 let progress = self.progress.notified();
471 if self.has_progress_since(completed) {
472 return;
473 }
474 progress.await;
475 }
476 }
477
478 fn progress_baseline(&self) -> Option<u64> {
479 let state = self.state.lock();
480 Self::has_work(&state).then_some(state.completed)
481 }
482
483 fn has_progress_since(&self, completed: u64) -> bool {
484 let state = self.state.lock();
485 state.completed != completed
486 || !Self::has_work(&state)
487 || state.phase == PacketPhase::Stopped
488 }
489
490 fn has_work(state: &PacketQueueState<K, T>) -> bool {
491 state.active != 0
492 || state
493 .lanes
494 .values()
495 .any(|lane| lane.pause_generation.is_none() && !lane.queued.is_empty())
496 }
497
498 fn pause_lane(&self, key: K) -> Option<PacketLanePause<K>> {
499 let mut state = self.state.lock();
500 if state.phase == PacketPhase::Stopped {
501 return None;
502 }
503 if state
504 .lanes
505 .get(&key)
506 .is_some_and(|lane| lane.pause_generation.is_some())
507 {
508 return None;
509 }
510
511 let generation = state.next_pause_generation;
512 assert!(
513 generation != u64::MAX,
514 "packet lane pause generation exhausted"
515 );
516 state.next_pause_generation = generation + 1;
517
518 let queued = {
519 let lane = state.lanes.entry(key).or_insert_with(PacketLane::new);
520 lane.pause_generation = Some(generation);
521 lane.queued.drain(..).collect::<Vec<_>>()
522 };
523
524 state.player_local_ready.retain(|entry| entry.0.1 != key);
525 state.serialized_ready.retain(|entry| entry.0.1 != key);
526 state.exclusive_ready.retain(|entry| entry.0.1 != key);
527 let queued_sequences = queued
528 .iter()
529 .map(|packet| packet.sequence)
530 .collect::<FxHashSet<_>>();
531 state
532 .serialized
533 .retain(|entry| !queued_sequences.contains(&entry.0));
534 state
535 .exclusive
536 .retain(|entry| !queued_sequences.contains(&entry.0));
537 let Some(lane) = state.lanes.get_mut(&key) else {
538 panic!("paused packet lane disappeared while deferring its packets");
539 };
540 lane.deferred
541 .extend(queued.into_iter().map(|packet| DeferredPacket {
542 execution: packet.execution,
543 admission_bytes: packet.admission_bytes,
544 value: packet.value,
545 }));
546
547 let is_idle = state.active == 0;
548 let should_wake = matches!(state.phase, PacketPhase::Open | PacketPhase::Draining(_))
549 && Self::next_ready_sequence(&state).is_some();
550 drop(state);
551 self.progress.notify_one();
552 if should_wake {
553 self.work_available.notify_all();
554 }
555 if is_idle {
556 self.idle.notify_one();
557 }
558 Some(PacketLanePause { key, generation })
559 }
560
561 fn resume_lane(&self, pause: PacketLanePause<K>) -> bool {
562 let mut state = self.state.lock();
563 if state.phase == PacketPhase::Stopped {
564 return false;
565 }
566
567 let Some(lane) = state.lanes.get_mut(&pause.key) else {
568 return false;
569 };
570 if lane.pause_generation != Some(pause.generation) {
571 return false;
572 }
573 lane.pause_generation = None;
574 let active = lane.active;
575 let deferred = mem::take(&mut lane.deferred);
576
577 let Ok(deferred_count) = u64::try_from(deferred.len()) else {
578 panic!("deferred packet count does not fit in the sequence space");
579 };
580 let first_sequence = state.next_sequence;
581 let Some(next_sequence) = first_sequence.checked_add(deferred_count) else {
582 panic!("packet submission sequence exhausted while resuming a lane");
583 };
584 state.next_sequence = next_sequence;
585
586 let mut queued = VecDeque::with_capacity(deferred.len());
590 for (sequence, packet) in (first_sequence..next_sequence).zip(deferred) {
591 Self::mark_global_order(&mut state, sequence, packet.execution);
592 queued.push_back(SequencedPacket {
593 sequence,
594 execution: packet.execution,
595 admission_bytes: packet.admission_bytes,
596 value: packet.value,
597 });
598 }
599 let first = queued
600 .front()
601 .map(|packet| (packet.sequence, packet.execution));
602 let Some(lane) = state.lanes.get_mut(&pause.key) else {
603 panic!("resumed packet lane disappeared before packet re-admission");
604 };
605 lane.queued.extend(queued);
606 if !active && let Some((sequence, execution)) = first {
607 Self::mark_ready(&mut state, sequence, pause.key, execution);
608 }
609 if !active && first.is_none() {
610 state.lanes.remove(&pause.key);
611 }
612
613 let should_wake = matches!(state.phase, PacketPhase::Open | PacketPhase::Draining(_))
614 && Self::next_ready_sequence(&state).is_some();
615 drop(state);
616 self.progress.notify_one();
617 if should_wake {
618 self.work_available.notify_all();
619 }
620 true
621 }
622
623 fn discard_lane(&self, key: K) {
624 let mut state = self.state.lock();
625 let (discarded_sequences, remove_lane) = {
626 let Some(lane) = state.lanes.get_mut(&key) else {
627 return;
628 };
629 let discarded_packets = lane.queued.len() + lane.deferred.len();
630 let discarded_bytes = lane
631 .queued
632 .iter()
633 .map(|packet| packet.admission_bytes)
634 .chain(lane.deferred.iter().map(|packet| packet.admission_bytes))
635 .sum::<usize>();
636 let discarded_sequences = lane
637 .queued
638 .iter()
639 .map(|packet| packet.sequence)
640 .collect::<FxHashSet<_>>();
641 lane.queued.clear();
642 lane.deferred.clear();
643 lane.pause_generation = None;
644 assert!(
645 lane.outstanding_packets >= discarded_packets,
646 "session packet admission accounting underflow while discarding"
647 );
648 lane.outstanding_packets -= discarded_packets;
649 assert!(
650 lane.outstanding_bytes >= discarded_bytes,
651 "session byte admission accounting underflow while discarding"
652 );
653 lane.outstanding_bytes -= discarded_bytes;
654 (discarded_sequences, !lane.active)
655 };
656
657 state.player_local_ready.retain(|entry| entry.0.1 != key);
658 state.serialized_ready.retain(|entry| entry.0.1 != key);
659 state.exclusive_ready.retain(|entry| entry.0.1 != key);
660 state
661 .serialized
662 .retain(|entry| !discarded_sequences.contains(&entry.0));
663 state
664 .exclusive
665 .retain(|entry| !discarded_sequences.contains(&entry.0));
666 if remove_lane {
667 state.lanes.remove(&key);
668 }
669
670 let is_idle = state.active == 0;
671 let should_wake = matches!(state.phase, PacketPhase::Open | PacketPhase::Draining(_))
672 && Self::next_ready_sequence(&state).is_some();
673 drop(state);
674 self.progress.notify_one();
675 if should_wake {
676 self.work_available.notify_all();
677 }
678 if is_idle {
679 self.idle.notify_one();
680 }
681 }
682
683 fn stop(&self) {
684 let mut state = self.state.lock();
685 state.phase = PacketPhase::Stopped;
686 state.player_local_ready.clear();
687 state.serialized_ready.clear();
688 state.exclusive_ready.clear();
689 state.serialized.clear();
690 state.exclusive.clear();
691 state.lanes.retain(|_, lane| {
692 let lane_discarded_packets = lane.queued.len() + lane.deferred.len();
693 let lane_discarded_bytes = lane
694 .queued
695 .iter()
696 .map(|packet| packet.admission_bytes)
697 .chain(lane.deferred.iter().map(|packet| packet.admission_bytes))
698 .sum::<usize>();
699 lane.queued.clear();
700 lane.deferred.clear();
701 assert!(
702 lane.outstanding_packets >= lane_discarded_packets,
703 "session packet admission accounting underflow while stopping"
704 );
705 lane.outstanding_packets -= lane_discarded_packets;
706 assert!(
707 lane.outstanding_bytes >= lane_discarded_bytes,
708 "session byte admission accounting underflow while stopping"
709 );
710 lane.outstanding_bytes -= lane_discarded_bytes;
711 lane.pause_generation = None;
712 lane.active
713 });
714 let is_idle = state.active == 0;
715 drop(state);
716 self.work_available.notify_all();
717 self.progress.notify_waiters();
718 if is_idle {
719 self.idle.notify_one();
720 }
721 }
722
723 fn next(&self) -> Option<PacketWork<'_, K, T>> {
724 let mut state = self.state.lock();
725 loop {
726 match state.phase {
727 PacketPhase::Stopped => return None,
728 PacketPhase::Open => {
729 if let Some((key, execution, admission_bytes, value)) =
730 Self::start_next(&mut state, None)
731 {
732 state.active += 1;
733 drop(state);
734 return Some(PacketWork {
735 value: Some(value),
736 key,
737 execution,
738 admission_bytes,
739 queue: self,
740 });
741 }
742 }
743 PacketPhase::Draining(before_sequence) => {
744 if let Some((key, execution, admission_bytes, value)) =
745 Self::start_next(&mut state, Some(before_sequence))
746 {
747 state.active += 1;
748 drop(state);
749 return Some(PacketWork {
750 value: Some(value),
751 key,
752 execution,
753 admission_bytes,
754 queue: self,
755 });
756 }
757 }
758 PacketPhase::Closed => {}
759 }
760 self.work_available.wait(&mut state);
761 }
762 }
763
764 #[cfg(test)]
765 fn try_next(&self) -> Option<PacketWork<'_, K, T>> {
766 let mut state = self.state.lock();
767 let before_sequence = match state.phase {
768 PacketPhase::Open => None,
769 PacketPhase::Draining(before_sequence) => Some(before_sequence),
770 PacketPhase::Closed | PacketPhase::Stopped => return None,
771 };
772 let (key, execution, admission_bytes, value) =
773 Self::start_next(&mut state, before_sequence)?;
774 state.active += 1;
775 drop(state);
776 Some(PacketWork {
777 value: Some(value),
778 key,
779 execution,
780 admission_bytes,
781 queue: self,
782 })
783 }
784
785 fn start_next(
786 state: &mut PacketQueueState<K, T>,
787 before_sequence: Option<u64>,
788 ) -> Option<(K, ScheduledPacketExecution, usize, T)> {
789 let (ready_sequence, key, execution) = Self::select_next(state, before_sequence)?;
790 let Some(lane) = state.lanes.get(&key) else {
791 panic!("ready packet lane disappeared before starting");
792 };
793 assert!(!lane.active, "ready packet lane is already active");
794 let Some(packet) = lane.queued.front() else {
795 panic!("ready packet lane has no queued packet");
796 };
797 assert_eq!(
798 ready_sequence, packet.sequence,
799 "ready packet sequence does not match lane front"
800 );
801 assert_eq!(
802 execution, packet.execution,
803 "packet is registered in the wrong ready queue"
804 );
805
806 match execution {
807 ScheduledPacketExecution::PlayerLocal => {
808 assert!(
809 state.player_local_ready.pop() == Some(Reverse((ready_sequence, key))),
810 "player-local ready queue changed while the queue lock was held"
811 );
812 }
813 ScheduledPacketExecution::Serialized => {
814 assert!(
815 state.serialized_ready.pop() == Some(Reverse((ready_sequence, key))),
816 "serialized ready queue changed while the queue lock was held"
817 );
818 assert_eq!(
819 state.serialized.pop(),
820 Some(Reverse(ready_sequence)),
821 "serialized packet order changed while the queue lock was held"
822 );
823 assert!(
824 !state.serialized_active,
825 "serialized packet started while another was active"
826 );
827 state.serialized_active = true;
828 }
829 ScheduledPacketExecution::Exclusive => {
830 assert!(
831 state.exclusive_ready.pop() == Some(Reverse((ready_sequence, key))),
832 "exclusive ready queue changed while the queue lock was held"
833 );
834 assert_eq!(
835 state.exclusive.pop(),
836 Some(Reverse(ready_sequence)),
837 "exclusive packet barrier changed while the queue lock was held"
838 );
839 state.exclusive_active = true;
840 }
841 }
842
843 let Some(lane) = state.lanes.get_mut(&key) else {
844 panic!("ready packet lane disappeared before removal");
845 };
846 let Some(packet) = lane.queued.pop_front() else {
847 panic!("ready packet lane has no queued packet during removal");
848 };
849 lane.active = true;
850 Some((key, execution, packet.admission_bytes, packet.value))
851 }
852
853 fn select_next(
854 state: &PacketQueueState<K, T>,
855 before_sequence: Option<u64>,
856 ) -> Option<(u64, K, ScheduledPacketExecution)> {
857 if state.exclusive_active {
858 return None;
859 }
860
861 let next_exclusive = state.exclusive.peek().map(|entry| entry.0);
862 let next_serialized = state.serialized.peek().map(|entry| entry.0);
863 let mut selected = None;
864 let mut consider = |entry: Option<&Reverse<(u64, K)>>,
865 execution: ScheduledPacketExecution| {
866 let Some(Reverse((sequence, key))) = entry.copied() else {
867 return;
868 };
869 if before_sequence.is_some_and(|cutoff| sequence >= cutoff)
870 || next_exclusive.is_some_and(|exclusive| exclusive < sequence)
871 {
872 return;
873 }
874 if selected.is_none_or(|(selected_sequence, _, _)| sequence < selected_sequence) {
875 selected = Some((sequence, key, execution));
876 }
877 };
878
879 consider(
880 state.player_local_ready.peek(),
881 ScheduledPacketExecution::PlayerLocal,
882 );
883 if !state.serialized_active
884 && state
885 .serialized_ready
886 .peek()
887 .is_some_and(|entry| Some(entry.0.0) == next_serialized)
888 {
889 consider(
890 state.serialized_ready.peek(),
891 ScheduledPacketExecution::Serialized,
892 );
893 }
894 if state.active == 0
895 && state
896 .exclusive_ready
897 .peek()
898 .is_some_and(|entry| Some(entry.0.0) == next_exclusive)
899 {
900 consider(
901 state.exclusive_ready.peek(),
902 ScheduledPacketExecution::Exclusive,
903 );
904 }
905 selected
906 }
907
908 fn finish_one(&self, key: K, execution: ScheduledPacketExecution, admission_bytes: usize) {
909 let mut state = self.state.lock();
910 assert!(state.active > 0, "packet work accounting underflow");
911 state.active -= 1;
912 state.completed = state.completed.wrapping_add(1);
913 match execution {
914 ScheduledPacketExecution::PlayerLocal => {}
915 ScheduledPacketExecution::Serialized => {
916 assert!(state.serialized_active, "serialized packet is not active");
917 state.serialized_active = false;
918 }
919 ScheduledPacketExecution::Exclusive => {
920 assert!(state.exclusive_active, "exclusive packet is not active");
921 state.exclusive_active = false;
922 }
923 }
924
925 let (next_packet, paused) = {
926 let Some(lane) = state.lanes.get_mut(&key) else {
927 panic!("active packet lane disappeared before completion");
928 };
929 assert!(lane.active, "completed packet lane is not active");
930 lane.active = false;
931 assert!(
932 lane.outstanding_packets > 0,
933 "session packet admission accounting underflow on completion"
934 );
935 lane.outstanding_packets -= 1;
936 assert!(
937 lane.outstanding_bytes >= admission_bytes,
938 "session byte admission accounting underflow on completion"
939 );
940 lane.outstanding_bytes -= admission_bytes;
941 (
942 lane.queued
943 .front()
944 .map(|packet| (packet.sequence, packet.execution)),
945 lane.pause_generation.is_some(),
946 )
947 };
948 if let Some((sequence, next_execution)) = next_packet {
949 if !paused {
950 Self::mark_ready(&mut state, sequence, key, next_execution);
951 }
952 } else if !paused {
953 let Some(lane) = state.lanes.get(&key) else {
954 panic!("completed packet lane disappeared before removal");
955 };
956 assert_eq!(lane.outstanding_packets, 0);
957 assert_eq!(lane.outstanding_bytes, 0);
958 state.lanes.remove(&key);
959 }
960
961 let is_idle = state.active == 0;
962 let should_wake = matches!(state.phase, PacketPhase::Open | PacketPhase::Draining(_))
963 && Self::next_ready_sequence(&state).is_some();
964 drop(state);
965 self.progress.notify_one();
966 if should_wake {
967 match execution {
968 ScheduledPacketExecution::Exclusive => {
969 self.work_available.notify_all();
970 }
971 ScheduledPacketExecution::PlayerLocal | ScheduledPacketExecution::Serialized => {
972 self.work_available.notify_one();
973 }
974 }
975 }
976 if is_idle {
977 self.idle.notify_one();
978 }
979 }
980
981 fn mark_ready(
982 state: &mut PacketQueueState<K, T>,
983 sequence: u64,
984 key: K,
985 execution: ScheduledPacketExecution,
986 ) {
987 let entry = Reverse((sequence, key));
988 match execution {
989 ScheduledPacketExecution::PlayerLocal => state.player_local_ready.push(entry),
990 ScheduledPacketExecution::Serialized => state.serialized_ready.push(entry),
991 ScheduledPacketExecution::Exclusive => state.exclusive_ready.push(entry),
992 }
993 }
994
995 fn mark_global_order(
996 state: &mut PacketQueueState<K, T>,
997 sequence: u64,
998 execution: ScheduledPacketExecution,
999 ) {
1000 match execution {
1001 ScheduledPacketExecution::PlayerLocal => {}
1002 ScheduledPacketExecution::Serialized => state.serialized.push(Reverse(sequence)),
1003 ScheduledPacketExecution::Exclusive => state.exclusive.push(Reverse(sequence)),
1004 }
1005 }
1006
1007 fn next_ready_sequence(state: &PacketQueueState<K, T>) -> Option<u64> {
1008 [
1009 state.player_local_ready.peek().map(|entry| entry.0.0),
1010 state.serialized_ready.peek().map(|entry| entry.0.0),
1011 state.exclusive_ready.peek().map(|entry| entry.0.0),
1012 ]
1013 .into_iter()
1014 .flatten()
1015 .min()
1016 }
1017}
1018
1019struct PacketWork<'a, K, T>
1020where
1021 K: Copy + Eq + Hash + Ord,
1022{
1023 value: Option<T>,
1024 key: K,
1025 execution: ScheduledPacketExecution,
1026 admission_bytes: usize,
1027 queue: &'a PacketQueue<K, T>,
1028}
1029
1030impl<K, T> PacketWork<'_, K, T>
1031where
1032 K: Copy + Eq + Hash + Ord,
1033{
1034 const fn take(&mut self) -> Option<T> {
1035 self.value.take()
1036 }
1037}
1038
1039impl<K, T> Drop for PacketWork<'_, K, T>
1040where
1041 K: Copy + Eq + Hash + Ord,
1042{
1043 fn drop(&mut self) {
1044 self.queue
1045 .finish_one(self.key, self.execution, self.admission_bytes);
1046 }
1047}
1048
1049#[cfg(test)]
1050mod tests {
1051 use std::{
1052 sync::{Arc, Weak, mpsc},
1053 thread,
1054 time::Duration,
1055 };
1056
1057 use steel_utils::TICKS_PER_SECOND;
1058 use tokio::time::timeout;
1059
1060 use crate::{
1061 entity::{Entity as _, LivingEntity as _},
1062 player::{ClientInformation, Player, connection::ScheduledPlayPacket},
1063 test_support::{TestPlayerBuilder, fresh_test_world},
1064 };
1065
1066 use super::{
1067 PACKET_ADMISSION_OVERHEAD, PacketAdmissionError, PacketAdmissionLimits, PacketProcessor,
1068 PacketQueue, PendingPlayPacket, ScheduledPacketExecution,
1069 };
1070
1071 const fn limits(per_player_packets: usize, per_player_bytes: usize) -> PacketAdmissionLimits {
1072 PacketAdmissionLimits {
1073 per_player_packets,
1074 per_player_bytes,
1075 }
1076 }
1077
1078 fn replacement_for(player: &Arc<Player>) -> Arc<Player> {
1079 Arc::new(Player::new(
1080 player.gameprofile.clone(),
1081 Arc::clone(&player.connection),
1082 Arc::clone(&player.session),
1083 player.get_world(),
1084 Weak::clone(&player.server),
1085 Arc::clone(&player.config),
1086 player.id(),
1087 ClientInformation::default(),
1088 ))
1089 }
1090
1091 #[test]
1092 fn per_player_packet_limit_rejects_before_assigning_a_sequence() {
1093 let queue = PacketQueue::with_limits(limits(2, 100));
1094 assert_eq!(
1095 queue.try_submit(1, ScheduledPacketExecution::PlayerLocal, 1, "first"),
1096 Ok(())
1097 );
1098 assert_eq!(
1099 queue.try_submit(1, ScheduledPacketExecution::Serialized, 1, "second"),
1100 Ok(())
1101 );
1102
1103 assert_eq!(
1104 queue.try_submit(1, ScheduledPacketExecution::Exclusive, 1, "rejected"),
1105 Err(PacketAdmissionError::PlayerPacketLimit)
1106 );
1107 assert_eq!(queue.state.lock().next_sequence, 2);
1108 assert_eq!(
1109 queue.try_submit(2, ScheduledPacketExecution::PlayerLocal, 1, "other player"),
1110 Ok(())
1111 );
1112 }
1113
1114 #[test]
1115 fn scheduled_respawn_is_retained_if_domain_switch_queues_before_worker_gate() {
1116 let world_fixture = fresh_test_world("scheduled_domain_switch_respawn_packet");
1117 let world = &world_fixture.world;
1118 let player = TestPlayerBuilder::new(Arc::clone(world), "RespawnTester", 1).build();
1119 let packet = ScheduledPlayPacket::perform_respawn_for_test();
1120 let Some(token) = player.begin_pending_world_change() else {
1121 panic!("test player should acquire a world-change token");
1122 };
1123 assert!(player.begin_domain_switch(token));
1124 player.set_health(0.0);
1125
1126 assert!(!PacketProcessor::packet_is_runnable(
1127 &player, &packet, false
1128 ));
1129 assert!(player.has_deferred_death_respawn_for_test());
1130
1131 assert!(player.finish_domain_switch(token));
1132 assert!(player.finish_pending_world_change(token));
1133 }
1134
1135 #[test]
1136 fn queued_packets_resolve_the_player_bound_when_they_start() {
1137 let world_fixture = fresh_test_world("packet_session_replacement_resolution");
1138 let world = &world_fixture.world;
1139 let original = TestPlayerBuilder::new(Arc::clone(world), "Original", 1).build();
1140 let replacement = replacement_for(&original);
1141 let session = Arc::clone(&original.session);
1142 let processor = PacketProcessor::new();
1143 let packet = ScheduledPlayPacket::perform_respawn_for_test();
1144
1145 processor.schedule(Arc::clone(&original), packet, 1);
1146 let Some(transition) = processor.pause_player_session(&session) else {
1147 panic!("session packet lane should pause");
1148 };
1149 assert!(session.replace_player(&original, &replacement));
1150
1151 processor.schedule(original, ScheduledPlayPacket::perform_respawn_for_test(), 1);
1154 assert!(processor.resume_player_session(transition));
1155 processor.queued.open();
1156
1157 for _ in 0..2 {
1158 let Some(mut work) = processor.queued.try_next() else {
1159 panic!("retained session packet should become runnable");
1160 };
1161 let Some(PendingPlayPacket { session, .. }) = work.take() else {
1162 panic!("retained packet should still carry its session");
1163 };
1164 let Some(current) = session.current_player() else {
1165 panic!("replacement should be bound before packet work resumes");
1166 };
1167 assert!(Arc::ptr_eq(¤t, &replacement));
1168 }
1169 }
1170
1171 #[test]
1172 fn per_player_byte_limit_is_independent_of_packet_count() {
1173 let queue = PacketQueue::with_limits(limits(10, 6));
1174 assert_eq!(
1175 queue.try_submit(1, ScheduledPacketExecution::PlayerLocal, 4, "first"),
1176 Ok(())
1177 );
1178
1179 assert_eq!(
1180 queue.try_submit(1, ScheduledPacketExecution::PlayerLocal, 3, "rejected"),
1181 Err(PacketAdmissionError::PlayerByteLimit)
1182 );
1183 assert_eq!(
1184 queue.try_submit(2, ScheduledPacketExecution::PlayerLocal, 6, "other player"),
1185 Ok(())
1186 );
1187 }
1188
1189 #[test]
1190 fn production_limit_allows_a_watchdog_window_of_mounted_client_traffic() {
1191 const VANILLA_WATCHDOG_SECONDS: usize = 60;
1192 const MOUNTED_PACKETS_PER_CLIENT_TICK: usize = 4;
1193 const EXPECTED_PACKETS: usize =
1194 VANILLA_WATCHDOG_SECONDS * TICKS_PER_SECOND as usize * MOUNTED_PACKETS_PER_CLIENT_TICK;
1195
1196 let queue = PacketQueue::new();
1197 for packet in 0..EXPECTED_PACKETS {
1198 assert!(
1199 queue
1200 .try_submit(
1201 1,
1202 ScheduledPacketExecution::PlayerLocal,
1203 PACKET_ADMISSION_OVERHEAD,
1204 packet,
1205 )
1206 .is_ok()
1207 );
1208 }
1209
1210 let state = queue.state.lock();
1211 let lane = state.lanes.get(&1).expect("session lane should exist");
1212 assert_eq!(lane.outstanding_packets, EXPECTED_PACKETS);
1213 }
1214
1215 #[test]
1216 fn independent_session_limits_do_not_accumulate_globally() {
1217 const SESSION_COUNT: usize = 64;
1218 const PACKETS_PER_SESSION: usize = 33;
1219
1220 let queue = PacketQueue::new();
1221 for session_id in 1_u64..=64 {
1222 for packet in 0..PACKETS_PER_SESSION {
1223 assert!(
1224 queue
1225 .try_submit(
1226 session_id,
1227 ScheduledPacketExecution::PlayerLocal,
1228 PACKET_ADMISSION_OVERHEAD,
1229 packet,
1230 )
1231 .is_ok()
1232 );
1233 }
1234 }
1235
1236 let state = queue.state.lock();
1237 assert_eq!(state.lanes.len(), SESSION_COUNT);
1238 assert_eq!(
1239 usize::try_from(state.next_sequence),
1240 Ok(SESSION_COUNT * PACKETS_PER_SESSION)
1241 );
1242 }
1243
1244 #[test]
1245 fn discarding_stale_session_keeps_replacement_session_work() {
1246 let stale_key = 1;
1247 let replacement_key = 2;
1248 let queue = PacketQueue::with_limits(limits(10, 100));
1249 queue.submit(
1250 stale_key,
1251 ScheduledPacketExecution::Exclusive,
1252 "stale barrier",
1253 );
1254 queue.submit(
1255 stale_key,
1256 ScheduledPacketExecution::Serialized,
1257 "stale serialized",
1258 );
1259 queue.submit(
1260 replacement_key,
1261 ScheduledPacketExecution::PlayerLocal,
1262 "replacement",
1263 );
1264
1265 queue.discard_lane(stale_key);
1266
1267 {
1268 let queue_state = queue.state.lock();
1269 assert!(!queue_state.lanes.contains_key(&stale_key));
1270 let replacement_lane = queue_state
1271 .lanes
1272 .get(&replacement_key)
1273 .expect("replacement session lane should remain");
1274 assert_eq!(replacement_lane.outstanding_packets, 1);
1275 assert_eq!(replacement_lane.outstanding_bytes, 1);
1276 assert!(queue_state.serialized.is_empty());
1277 assert!(queue_state.exclusive.is_empty());
1278 }
1279
1280 queue.open();
1281 let Some(mut work) = queue.try_next() else {
1282 panic!("replacement session packet should remain runnable");
1283 };
1284 assert_eq!(work.take(), Some("replacement"));
1285 }
1286
1287 #[test]
1288 fn paused_fifo_is_re_admitted_after_unrelated_global_work() {
1289 let queue = PacketQueue::with_limits(limits(10, 100));
1290 queue.submit(
1291 1,
1292 ScheduledPacketExecution::Serialized,
1293 "deferred serialized",
1294 );
1295 queue.submit(1, ScheduledPacketExecution::Exclusive, "deferred exclusive");
1296 let Some(pause) = queue.pause_lane(1) else {
1297 panic!("session lane should pause");
1298 };
1299
1300 queue.submit(
1301 2,
1302 ScheduledPacketExecution::Serialized,
1303 "unrelated serialized",
1304 );
1305 queue.submit(
1306 3,
1307 ScheduledPacketExecution::Exclusive,
1308 "unrelated exclusive",
1309 );
1310 queue.open();
1311
1312 let Some(mut unrelated_serialized) = queue.try_next() else {
1313 panic!("paused serialized work must not block another session");
1314 };
1315 assert_eq!(unrelated_serialized.take(), Some("unrelated serialized"));
1316 drop(unrelated_serialized);
1317
1318 let Some(mut unrelated_exclusive) = queue.try_next() else {
1319 panic!("paused exclusive work must not remain a global barrier");
1320 };
1321 assert_eq!(unrelated_exclusive.take(), Some("unrelated exclusive"));
1322 drop(unrelated_exclusive);
1323 assert!(queue.try_next().is_none());
1324
1325 assert!(queue.resume_lane(pause));
1326 let Some(mut deferred_serialized) = queue.try_next() else {
1327 panic!("resumed FIFO front should become runnable");
1328 };
1329 assert_eq!(deferred_serialized.take(), Some("deferred serialized"));
1330 drop(deferred_serialized);
1331
1332 let Some(mut deferred_exclusive) = queue.try_next() else {
1333 panic!("resumed FIFO tail should follow its front");
1334 };
1335 assert_eq!(deferred_exclusive.take(), Some("deferred exclusive"));
1336 }
1337
1338 #[test]
1339 fn pausing_active_player_session_defers_its_tail_until_exact_resume() {
1340 let world_fixture = fresh_test_world("packet_active_session_pause");
1341 let world = &world_fixture.world;
1342 let player = TestPlayerBuilder::new(Arc::clone(world), "Paused", 1).build();
1343 let unrelated_player = TestPlayerBuilder::new(Arc::clone(world), "Unrelated", 2).build();
1344 let processor = PacketProcessor::new();
1345 processor.schedule(
1346 Arc::clone(&player),
1347 ScheduledPlayPacket::perform_respawn_for_test(),
1348 1,
1349 );
1350 processor.schedule(
1351 Arc::clone(&player),
1352 ScheduledPlayPacket::perform_respawn_for_test(),
1353 2,
1354 );
1355 processor.open_after_tick();
1356
1357 let Some(mut active) = processor.queued.try_next() else {
1358 panic!("respawn packet should be active before its lane pauses");
1359 };
1360 assert_eq!(active.admission_bytes, PACKET_ADMISSION_OVERHEAD + 1);
1361 let Some(PendingPlayPacket {
1362 session: active_session,
1363 ..
1364 }) = active.take()
1365 else {
1366 panic!("active respawn should carry its player session");
1367 };
1368 assert!(Arc::ptr_eq(&active_session, &player.session));
1369
1370 let Some(transition) = processor.pause_player_session(&player.session) else {
1371 panic!("active session lane should pause");
1372 };
1373 processor.schedule(
1374 Arc::clone(&player),
1375 ScheduledPlayPacket::perform_respawn_for_test(),
1376 3,
1377 );
1378 processor.schedule(
1379 Arc::clone(&unrelated_player),
1380 ScheduledPlayPacket::perform_respawn_for_test(),
1381 4,
1382 );
1383
1384 let Some(mut unrelated) = processor.queued.try_next() else {
1385 panic!("paused lane must not block unrelated player work");
1386 };
1387 assert_eq!(unrelated.admission_bytes, PACKET_ADMISSION_OVERHEAD + 4);
1388 let Some(PendingPlayPacket {
1389 session: unrelated_session,
1390 ..
1391 }) = unrelated.take()
1392 else {
1393 panic!("unrelated packet should carry its player session");
1394 };
1395 assert!(Arc::ptr_eq(&unrelated_session, &unrelated_player.session));
1396 drop(unrelated);
1397 assert!(processor.queued.try_next().is_none());
1398
1399 assert!(processor.resume_player_session(transition));
1400 assert!(processor.queued.try_next().is_none());
1401 drop(active);
1402
1403 let Some(mut queued_before_pause) = processor.queued.try_next() else {
1404 panic!("exact pause owner should re-admit the original lane tail");
1405 };
1406 assert_eq!(
1407 queued_before_pause.admission_bytes,
1408 PACKET_ADMISSION_OVERHEAD + 2
1409 );
1410 let Some(PendingPlayPacket {
1411 session: queued_before_pause_session,
1412 ..
1413 }) = queued_before_pause.take()
1414 else {
1415 panic!("packet queued before the pause should retain its session");
1416 };
1417 assert!(Arc::ptr_eq(&queued_before_pause_session, &player.session));
1418 drop(queued_before_pause);
1419
1420 let Some(mut queued_during_pause) = processor.queued.try_next() else {
1421 panic!("packets submitted while paused should retain session FIFO order");
1422 };
1423 assert_eq!(
1424 queued_during_pause.admission_bytes,
1425 PACKET_ADMISSION_OVERHEAD + 3
1426 );
1427 let Some(PendingPlayPacket {
1428 session: queued_during_pause_session,
1429 ..
1430 }) = queued_during_pause.take()
1431 else {
1432 panic!("packet queued during the pause should retain its session");
1433 };
1434 assert!(Arc::ptr_eq(&queued_during_pause_session, &player.session));
1435 }
1436
1437 #[test]
1438 fn stale_pause_token_cannot_resume_a_later_pause() {
1439 let queue = PacketQueue::with_limits(limits(10, 100));
1440 let Some(first_pause) = queue.pause_lane(1) else {
1441 panic!("first lane pause should succeed");
1442 };
1443 assert!(queue.resume_lane(first_pause));
1444
1445 let Some(second_pause) = queue.pause_lane(1) else {
1446 panic!("second lane pause should succeed");
1447 };
1448 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "deferred");
1449 queue.open();
1450
1451 assert!(!queue.resume_lane(first_pause));
1452 assert!(queue.try_next().is_none());
1453 assert!(queue.resume_lane(second_pause));
1454
1455 let Some(mut resumed) = queue.try_next() else {
1456 panic!("current pause owner should resume the lane");
1457 };
1458 assert_eq!(resumed.take(), Some("deferred"));
1459 }
1460
1461 #[tokio::test]
1462 async fn paused_packets_do_not_hold_tick_drain_open() {
1463 let queue = PacketQueue::new();
1464 queue.submit(1, ScheduledPacketExecution::Exclusive, "deferred");
1465 let Some(pause) = queue.pause_lane(1) else {
1466 panic!("session lane should pause");
1467 };
1468 queue.open();
1469
1470 assert!(
1471 timeout(Duration::from_secs(1), queue.drain_for_tick())
1472 .await
1473 .is_ok()
1474 );
1475 assert!(queue.resume_lane(pause));
1476 queue.open();
1477
1478 let Some(mut resumed) = queue.try_next() else {
1479 panic!("deferred packet should remain retained after the tick drain");
1480 };
1481 assert_eq!(resumed.take(), Some("deferred"));
1482 }
1483
1484 #[test]
1485 fn active_packet_remains_charged_until_completion() {
1486 let queue = PacketQueue::with_limits(limits(1, 4));
1487 assert_eq!(
1488 queue.try_submit(1, ScheduledPacketExecution::PlayerLocal, 4, "active"),
1489 Ok(())
1490 );
1491 queue.open();
1492 let Some(work) = queue.try_next() else {
1493 panic!("accepted packet should start");
1494 };
1495
1496 assert_eq!(
1497 queue.try_submit(1, ScheduledPacketExecution::PlayerLocal, 1, "rejected"),
1498 Err(PacketAdmissionError::PlayerPacketLimit)
1499 );
1500 {
1501 let state = queue.state.lock();
1502 let lane = state.lanes.get(&1).expect("active lane should remain");
1503 assert_eq!(lane.outstanding_packets, 1);
1504 assert_eq!(lane.outstanding_bytes, 4);
1505 }
1506
1507 drop(work);
1508 {
1509 let state = queue.state.lock();
1510 assert!(state.lanes.is_empty());
1511 }
1512 assert_eq!(
1513 queue.try_submit(1, ScheduledPacketExecution::PlayerLocal, 4, "next"),
1514 Ok(())
1515 );
1516 }
1517
1518 #[test]
1519 fn discarding_lane_removes_hidden_barriers_and_keeps_active_work_charged() {
1520 let queue = PacketQueue::with_limits(limits(10, 100));
1521 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "active attacker");
1522 queue.submit(1, ScheduledPacketExecution::Serialized, "hidden serialized");
1523 queue.submit(1, ScheduledPacketExecution::Exclusive, "hidden exclusive");
1524 queue.submit(2, ScheduledPacketExecution::PlayerLocal, "other player");
1525 queue.open();
1526 let Some(mut active) = queue.try_next() else {
1527 panic!("attacker's first packet should start");
1528 };
1529 assert_eq!(active.take(), Some("active attacker"));
1530
1531 queue.discard_lane(1);
1532
1533 {
1534 let state = queue.state.lock();
1535 assert!(state.serialized.is_empty());
1536 assert!(state.exclusive.is_empty());
1537 assert!(state.serialized_ready.is_empty());
1538 assert!(state.exclusive_ready.is_empty());
1539 let Some(lane) = state.lanes.get(&1) else {
1540 panic!("active attacker lane should remain");
1541 };
1542 assert!(lane.active);
1543 assert!(lane.queued.is_empty());
1544 assert_eq!(lane.outstanding_packets, 1);
1545 assert_eq!(lane.outstanding_bytes, 1);
1546 }
1547
1548 let Some(mut other) = queue.try_next() else {
1549 panic!("purged barriers should no longer block another player");
1550 };
1551 assert_eq!(other.take(), Some("other player"));
1552 drop(other);
1553 drop(active);
1554
1555 let state = queue.state.lock();
1556 assert!(state.lanes.is_empty());
1557 }
1558
1559 #[test]
1560 fn discarding_idle_lane_removes_ready_exclusive_barrier() {
1561 let queue = PacketQueue::with_limits(limits(10, 100));
1562 queue.submit(1, ScheduledPacketExecution::Exclusive, "attacker barrier");
1563 queue.submit(1, ScheduledPacketExecution::Serialized, "hidden serialized");
1564 queue.submit(2, ScheduledPacketExecution::PlayerLocal, "other player");
1565
1566 queue.discard_lane(1);
1567
1568 {
1569 let state = queue.state.lock();
1570 assert!(!state.lanes.contains_key(&1));
1571 let other = state.lanes.get(&2).expect("other lane should remain");
1572 assert_eq!(other.outstanding_packets, 1);
1573 assert_eq!(other.outstanding_bytes, 1);
1574 assert!(state.exclusive_ready.is_empty());
1575 assert!(state.exclusive.is_empty());
1576 assert!(state.serialized.is_empty());
1577 }
1578
1579 queue.open();
1580 let Some(mut other) = queue.try_next() else {
1581 panic!("discarded exclusive barrier should not block another player");
1582 };
1583 assert_eq!(other.take(), Some("other player"));
1584 }
1585
1586 #[test]
1587 fn queued_packets_start_in_submission_order_when_opened() {
1588 let queue = PacketQueue::new();
1589 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 1);
1590 queue.submit(2, ScheduledPacketExecution::PlayerLocal, 2);
1591 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 3);
1592 assert!(queue.try_next().is_none());
1593
1594 queue.open();
1595 let mut processed = Vec::new();
1596 while let Some(mut work) = queue.try_next() {
1597 if let Some(value) = work.take() {
1598 processed.push(value);
1599 }
1600 }
1601
1602 assert_eq!(processed, [1, 2, 3]);
1603 }
1604
1605 #[test]
1606 fn packet_lane_only_allows_one_active_handler() {
1607 let queue = PacketQueue::new();
1608 queue.submit(
1609 1,
1610 ScheduledPacketExecution::PlayerLocal,
1611 "first player packet",
1612 );
1613 queue.submit(
1614 1,
1615 ScheduledPacketExecution::PlayerLocal,
1616 "second player packet",
1617 );
1618 queue.submit(
1619 2,
1620 ScheduledPacketExecution::PlayerLocal,
1621 "other player packet",
1622 );
1623 queue.open();
1624
1625 let Some(mut first) = queue.try_next() else {
1626 panic!("first packet should start");
1627 };
1628 assert_eq!(first.take(), Some("first player packet"));
1629
1630 let Some(mut other_player) = queue.try_next() else {
1631 panic!("another player's packet should be able to start");
1632 };
1633 assert_eq!(other_player.take(), Some("other player packet"));
1634 assert!(queue.try_next().is_none());
1635
1636 drop(first);
1637 let Some(mut second) = queue.try_next() else {
1638 panic!("the next packet should start after its lane becomes idle");
1639 };
1640 assert_eq!(second.take(), Some("second player packet"));
1641 }
1642
1643 #[test]
1644 fn serialized_packet_overlaps_player_local_work_but_not_another_serialized_packet() {
1645 let queue = PacketQueue::new();
1646 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "first local");
1647 queue.submit(2, ScheduledPacketExecution::Serialized, "first serialized");
1648 queue.submit(3, ScheduledPacketExecution::Serialized, "second serialized");
1649 queue.submit(4, ScheduledPacketExecution::PlayerLocal, "later local");
1650 queue.open();
1651
1652 let Some(mut first_local) = queue.try_next() else {
1653 panic!("first player-local packet should start");
1654 };
1655 assert_eq!(first_local.take(), Some("first local"));
1656
1657 let Some(mut first_serialized) = queue.try_next() else {
1658 panic!("serialized packet should overlap player-local work");
1659 };
1660 assert_eq!(first_serialized.take(), Some("first serialized"));
1661
1662 let Some(mut later_local) = queue.try_next() else {
1663 panic!("player-local work should bypass a blocked serialized packet");
1664 };
1665 assert_eq!(later_local.take(), Some("later local"));
1666 assert!(queue.try_next().is_none());
1667
1668 drop(first_serialized);
1669 let Some(mut second_serialized) = queue.try_next() else {
1670 panic!("next serialized packet should start after its predecessor finishes");
1671 };
1672 assert_eq!(second_serialized.take(), Some("second serialized"));
1673 }
1674
1675 #[test]
1676 fn serialized_packet_hidden_in_an_active_lane_preserves_serialized_order() {
1677 let queue = PacketQueue::new();
1678 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "active lane");
1679 queue.submit(1, ScheduledPacketExecution::Serialized, "hidden serialized");
1680 queue.submit(2, ScheduledPacketExecution::Serialized, "later serialized");
1681 queue.submit(
1682 3,
1683 ScheduledPacketExecution::PlayerLocal,
1684 "independent local",
1685 );
1686 queue.open();
1687
1688 let Some(active_lane) = queue.try_next() else {
1689 panic!("first lane packet should start");
1690 };
1691 let Some(mut independent_local) = queue.try_next() else {
1692 panic!("player-local work should bypass serialized ordering contention");
1693 };
1694 assert_eq!(independent_local.take(), Some("independent local"));
1695 assert!(queue.try_next().is_none());
1696
1697 drop(active_lane);
1698 let Some(mut hidden_serialized) = queue.try_next() else {
1699 panic!("earliest serialized packet should start when its lane becomes idle");
1700 };
1701 assert_eq!(hidden_serialized.take(), Some("hidden serialized"));
1702 assert!(queue.try_next().is_none());
1703
1704 drop(hidden_serialized);
1705 let Some(mut later_serialized) = queue.try_next() else {
1706 panic!("later serialized packet should preserve global submission order");
1707 };
1708 assert_eq!(later_serialized.take(), Some("later serialized"));
1709 }
1710
1711 #[test]
1712 fn blocking_worker_bypasses_active_serialized_work_for_player_local_work() {
1713 let queue = Arc::new(PacketQueue::new());
1714 queue.submit(1, ScheduledPacketExecution::Serialized, "active serialized");
1715 queue.submit(2, ScheduledPacketExecution::Serialized, "queued serialized");
1716 queue.submit(3, ScheduledPacketExecution::PlayerLocal, "player local");
1717 queue.open();
1718
1719 let Some(active_serialized) = queue.try_next() else {
1720 panic!("first serialized packet should start");
1721 };
1722 let worker_queue = Arc::clone(&queue);
1723 let (sender, receiver) = mpsc::channel();
1724 let worker = thread::spawn(move || {
1725 for _ in 0..2 {
1726 let Some(mut work) = worker_queue.next() else {
1727 return;
1728 };
1729 if let Some(value) = work.take() {
1730 let _ = sender.send(value);
1731 }
1732 }
1733 });
1734
1735 assert_eq!(
1736 receiver.recv_timeout(Duration::from_secs(1)),
1737 Ok("player local")
1738 );
1739 assert!(receiver.recv_timeout(Duration::from_millis(10)).is_err());
1740
1741 drop(active_serialized);
1742 assert_eq!(
1743 receiver.recv_timeout(Duration::from_secs(1)),
1744 Ok("queued serialized")
1745 );
1746 assert!(worker.join().is_ok());
1747 }
1748
1749 #[test]
1750 fn exclusive_packet_waits_for_active_work_and_blocks_later_packets() {
1751 let queue = PacketQueue::new();
1752 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "before barrier");
1753 queue.submit(2, ScheduledPacketExecution::Exclusive, "barrier");
1754 queue.submit(3, ScheduledPacketExecution::PlayerLocal, "after barrier");
1755 queue.open();
1756
1757 let Some(mut before) = queue.try_next() else {
1758 panic!("packet before the barrier should start");
1759 };
1760 assert_eq!(before.take(), Some("before barrier"));
1761 assert!(queue.try_next().is_none());
1762
1763 drop(before);
1764 let Some(mut barrier) = queue.try_next() else {
1765 panic!("exclusive packet should start after active work finishes");
1766 };
1767 assert_eq!(barrier.take(), Some("barrier"));
1768 assert!(queue.try_next().is_none());
1769
1770 drop(barrier);
1771 let Some(mut after) = queue.try_next() else {
1772 panic!("packet after the barrier should start after it finishes");
1773 };
1774 assert_eq!(after.take(), Some("after barrier"));
1775 }
1776
1777 #[test]
1778 fn exclusive_packet_waits_for_serialized_work_and_blocks_player_local_work() {
1779 let queue = PacketQueue::new();
1780 queue.submit(1, ScheduledPacketExecution::Serialized, "before barrier");
1781 queue.submit(2, ScheduledPacketExecution::Exclusive, "barrier");
1782 queue.submit(3, ScheduledPacketExecution::PlayerLocal, "after barrier");
1783 queue.open();
1784
1785 let Some(mut before) = queue.try_next() else {
1786 panic!("serialized packet before the barrier should start");
1787 };
1788 assert_eq!(before.take(), Some("before barrier"));
1789 assert!(queue.try_next().is_none());
1790
1791 drop(before);
1792 let Some(mut barrier) = queue.try_next() else {
1793 panic!("exclusive packet should wait for serialized work");
1794 };
1795 assert_eq!(barrier.take(), Some("barrier"));
1796 assert!(queue.try_next().is_none());
1797
1798 drop(barrier);
1799 let Some(mut after) = queue.try_next() else {
1800 panic!("player-local packet should wait for the exclusive barrier");
1801 };
1802 assert_eq!(after.take(), Some("after barrier"));
1803 }
1804
1805 #[test]
1806 fn exclusive_packet_hidden_in_an_active_lane_still_blocks_later_lanes() {
1807 let queue = PacketQueue::new();
1808 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "active");
1809 queue.submit(1, ScheduledPacketExecution::Exclusive, "barrier");
1810 queue.submit(2, ScheduledPacketExecution::PlayerLocal, "later lane");
1811 queue.open();
1812
1813 let Some(active) = queue.try_next() else {
1814 panic!("first packet should start");
1815 };
1816 assert!(queue.try_next().is_none());
1817
1818 drop(active);
1819 let Some(mut barrier) = queue.try_next() else {
1820 panic!("hidden exclusive packet should become runnable");
1821 };
1822 assert_eq!(barrier.take(), Some("barrier"));
1823 }
1824
1825 #[test]
1826 fn closed_phase_retains_new_packets_for_the_next_open_phase() {
1827 let queue = PacketQueue::new();
1828 queue.open();
1829 queue.close();
1830 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 1);
1831
1832 assert!(queue.try_next().is_none());
1833 queue.open();
1834 let Some(mut work) = queue.try_next() else {
1835 panic!("queued packet should become available when the packet phase opens");
1836 };
1837 assert_eq!(work.take(), Some(1));
1838 }
1839
1840 #[test]
1841 fn tick_drain_only_processes_packets_submitted_before_its_cutoff() {
1842 let queue = PacketQueue::new();
1843 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "before cutoff");
1844 queue.open();
1845 let Some((before_sequence, should_wake)) = queue.begin_tick_drain() else {
1846 panic!("running queue should begin a tick drain");
1847 };
1848 assert!(should_wake);
1849 queue.submit(2, ScheduledPacketExecution::PlayerLocal, "after cutoff");
1850
1851 let Some(mut before) = queue.try_next() else {
1852 panic!("packet before the cutoff should drain");
1853 };
1854 assert_eq!(before.take(), Some("before cutoff"));
1855 drop(before);
1856
1857 assert!(queue.try_next().is_none());
1858 assert!(queue.tick_drain_complete(before_sequence));
1859 queue.finish_tick_drain(before_sequence);
1860 queue.open();
1861 let Some(mut after) = queue.try_next() else {
1862 panic!("packet after the cutoff should wait for the next open phase");
1863 };
1864 assert_eq!(after.take(), Some("after cutoff"));
1865 }
1866
1867 #[test]
1868 fn tick_drain_orders_hidden_serialized_and_exclusive_work_before_its_cutoff() {
1869 let queue = PacketQueue::new();
1870 queue.submit(1, ScheduledPacketExecution::PlayerLocal, "active local");
1871 queue.submit(1, ScheduledPacketExecution::Serialized, "hidden serialized");
1872 queue.submit(2, ScheduledPacketExecution::Exclusive, "exclusive");
1873 queue.open();
1874
1875 let Some(active_local) = queue.try_next() else {
1876 panic!("player-local packet should start before draining");
1877 };
1878 let Some((before_sequence, should_wake)) = queue.begin_tick_drain() else {
1879 panic!("running queue should begin a tick drain");
1880 };
1881 assert!(!should_wake);
1882 queue.submit(
1883 3,
1884 ScheduledPacketExecution::Serialized,
1885 "post-cutoff serialized",
1886 );
1887 queue.submit(
1888 4,
1889 ScheduledPacketExecution::PlayerLocal,
1890 "post-cutoff local",
1891 );
1892
1893 assert!(!queue.tick_drain_complete(before_sequence));
1894 assert!(queue.try_next().is_none());
1895 drop(active_local);
1896
1897 let Some(mut hidden_serialized) = queue.try_next() else {
1898 panic!("hidden pre-cutoff serialized packet should drain");
1899 };
1900 assert_eq!(hidden_serialized.take(), Some("hidden serialized"));
1901 assert!(!queue.tick_drain_complete(before_sequence));
1902 drop(hidden_serialized);
1903
1904 let Some(mut exclusive) = queue.try_next() else {
1905 panic!("pre-cutoff exclusive packet should drain after earlier work");
1906 };
1907 assert_eq!(exclusive.take(), Some("exclusive"));
1908 assert!(!queue.tick_drain_complete(before_sequence));
1909 drop(exclusive);
1910
1911 assert!(queue.try_next().is_none());
1912 assert!(queue.tick_drain_complete(before_sequence));
1913 queue.finish_tick_drain(before_sequence);
1914 queue.open();
1915
1916 let Some(mut post_cutoff_serialized) = queue.try_next() else {
1917 panic!("post-cutoff serialized packet should remain for the next phase");
1918 };
1919 assert_eq!(
1920 post_cutoff_serialized.take(),
1921 Some("post-cutoff serialized")
1922 );
1923 let Some(mut post_cutoff_local) = queue.try_next() else {
1924 panic!("post-cutoff player-local packet should remain for the next phase");
1925 };
1926 assert_eq!(post_cutoff_local.take(), Some("post-cutoff local"));
1927 }
1928
1929 #[tokio::test]
1930 async fn tick_drain_waits_for_active_packet_work() {
1931 let queue = Arc::new(PacketQueue::new());
1932 queue.open();
1933 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 1);
1934 let Some(work) = queue.try_next() else {
1935 panic!("open packet phase should start queued work");
1936 };
1937 let drain = queue.drain_for_tick();
1938 tokio::pin!(drain);
1939
1940 assert!(
1941 timeout(Duration::from_millis(10), drain.as_mut())
1942 .await
1943 .is_err()
1944 );
1945 drop(work);
1946 assert!(
1947 timeout(Duration::from_secs(1), drain.as_mut())
1948 .await
1949 .is_ok()
1950 );
1951 }
1952
1953 #[tokio::test]
1954 async fn overload_progress_waits_for_one_active_packet_to_finish() {
1955 let queue = Arc::new(PacketQueue::new());
1956 queue.open();
1957 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 1);
1958 let Some(work) = queue.try_next() else {
1959 panic!("open packet phase should start queued work");
1960 };
1961 let Some(completed) = queue.progress_baseline() else {
1962 panic!("active packet work should require progress");
1963 };
1964 let progress = queue.wait_for_progress_since(completed);
1965 tokio::pin!(progress);
1966
1967 assert!(
1968 timeout(Duration::from_millis(10), progress.as_mut())
1969 .await
1970 .is_err()
1971 );
1972 drop(work);
1973 assert!(
1974 timeout(Duration::from_secs(1), progress.as_mut())
1975 .await
1976 .is_ok()
1977 );
1978 }
1979
1980 #[test]
1981 fn stopped_queue_discards_pending_and_future_work() {
1982 let queue = PacketQueue::new();
1983 queue.open();
1984 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 1);
1985 queue.stop();
1986 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 2);
1987
1988 assert!(queue.try_next().is_none());
1989 let state = queue.state.lock();
1990 assert!(state.lanes.is_empty());
1991 }
1992
1993 #[test]
1994 fn stopping_with_active_work_keeps_completion_accounting_valid() {
1995 let queue = PacketQueue::new();
1996 queue.submit(1, ScheduledPacketExecution::Exclusive, 1);
1997 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 2);
1998 queue.open();
1999 let Some(work) = queue.try_next() else {
2000 panic!("open packet phase should start queued work");
2001 };
2002
2003 queue.stop();
2004 {
2005 let state = queue.state.lock();
2006 let lane = state.lanes.get(&1).expect("active lane should remain");
2007 assert!(lane.active);
2008 assert!(lane.queued.is_empty());
2009 assert_eq!(lane.outstanding_packets, 1);
2010 assert_eq!(lane.outstanding_bytes, 1);
2011 }
2012 drop(work);
2013
2014 assert!(queue.try_next().is_none());
2015 let state = queue.state.lock();
2016 assert_eq!(state.active, 0);
2017 assert!(state.lanes.is_empty());
2018 }
2019
2020 #[tokio::test]
2021 async fn stopping_with_active_serialized_and_local_work_clears_all_queue_state() {
2022 let queue = PacketQueue::new();
2023 queue.submit(1, ScheduledPacketExecution::Serialized, "active serialized");
2024 queue.submit(2, ScheduledPacketExecution::PlayerLocal, "active local");
2025 queue.submit(3, ScheduledPacketExecution::Serialized, "queued serialized");
2026 queue.submit(4, ScheduledPacketExecution::Exclusive, "queued exclusive");
2027 queue.open();
2028
2029 let Some(active_serialized) = queue.try_next() else {
2030 panic!("serialized packet should start");
2031 };
2032 let Some(active_local) = queue.try_next() else {
2033 panic!("player-local packet should overlap serialized work");
2034 };
2035 queue.stop();
2036 queue.drain_for_tick().await;
2037
2038 {
2039 let state = queue.state.lock();
2040 assert_eq!(state.active, 2);
2041 assert!(state.serialized_active);
2042 assert!(state.player_local_ready.is_empty());
2043 assert!(state.serialized_ready.is_empty());
2044 assert!(state.exclusive_ready.is_empty());
2045 assert!(state.serialized.is_empty());
2046 assert!(state.exclusive.is_empty());
2047 assert_eq!(state.lanes.len(), 2);
2048 for key in [1, 2] {
2049 let lane = state.lanes.get(&key).expect("active lane should remain");
2050 assert!(lane.active);
2051 assert!(lane.queued.is_empty());
2052 assert_eq!(lane.outstanding_packets, 1);
2053 assert_eq!(lane.outstanding_bytes, 1);
2054 }
2055 }
2056
2057 drop(active_serialized);
2058 drop(active_local);
2059
2060 let state = queue.state.lock();
2061 assert_eq!(state.active, 0);
2062 assert!(!state.serialized_active);
2063 assert!(state.lanes.is_empty());
2064 }
2065
2066 #[test]
2067 fn blocking_worker_only_starts_work_during_the_open_phase() {
2068 let queue = Arc::new(PacketQueue::new());
2069 let worker_queue = Arc::clone(&queue);
2070 let (sender, receiver) = mpsc::channel();
2071 let worker = thread::spawn(move || {
2072 while let Some(mut work) = worker_queue.next() {
2073 if let Some(value) = work.take() {
2074 let _ = sender.send(value);
2075 }
2076 }
2077 });
2078
2079 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 1);
2080 assert!(receiver.recv_timeout(Duration::from_millis(10)).is_err());
2081 queue.open();
2082 assert_eq!(receiver.recv_timeout(Duration::from_secs(1)), Ok(1));
2083 queue.close();
2084 queue.submit(1, ScheduledPacketExecution::PlayerLocal, 2);
2085 assert!(receiver.recv_timeout(Duration::from_millis(10)).is_err());
2086 queue.stop();
2087
2088 assert!(worker.join().is_ok());
2089 }
2090}