box3d_rust/recording/dispatch/
mod.rs1use crate::id::{BodyId, JointId, ShapeId};
7use crate::recording::buffer::SnapReader;
8use crate::recording::ops::RecOp;
9use crate::recording::registry::RegistrySlot;
10use crate::recording::session::RecTag;
11use crate::world::World;
12
13use super::player::RecPlayer;
14
15pub struct RecReader<'a> {
17 pub data: &'a [u8],
18 pub size: i32,
19 pub cursor: i32,
20 pub ok: bool,
21 pub diverged: bool,
22 pub world: *mut World,
23 pub owner: Option<*mut RecPlayer>,
24 pub slots: Vec<RegistrySlot>,
25 pub tags: Vec<RecTag>,
26 pub pending_query_key: u64,
27 pub pending_body_create: Option<BodyId>,
28 pub pending_body_destroy: Option<BodyId>,
29}
30
31impl<'a> RecReader<'a> {
32 pub fn new(data: &'a [u8]) -> Self {
33 Self {
34 data,
35 size: data.len() as i32,
36 cursor: 0,
37 ok: true,
38 diverged: false,
39 world: std::ptr::null_mut(),
40 owner: None,
41 slots: Vec::new(),
42 tags: Vec::new(),
43 pending_query_key: 0,
44 pending_body_create: None,
45 pending_body_destroy: None,
46 }
47 }
48
49 pub fn snap(&mut self) -> SnapReader<'a> {
50 let mut r = SnapReader::new(self.data);
51 r.set_cursor(self.cursor as usize);
52 r.ok = self.ok;
53 r
54 }
55
56 pub fn sync_from(&mut self, r: &SnapReader<'_>) {
57 self.cursor = r.cursor() as i32;
58 self.ok = r.ok;
59 }
60
61 pub fn make_body_id(&self, recorded: BodyId) -> BodyId {
62 let world = unsafe { &*self.world };
63 BodyId {
64 index1: recorded.index1,
65 world0: world.world_id,
66 generation: recorded.generation,
67 }
68 }
69
70 pub fn make_shape_id(&self, recorded: ShapeId) -> ShapeId {
71 let world = unsafe { &*self.world };
72 ShapeId {
73 index1: recorded.index1,
74 world0: world.world_id,
75 generation: recorded.generation,
76 }
77 }
78
79 pub fn make_joint_id(&self, recorded: JointId) -> JointId {
80 let world = unsafe { &*self.world };
81 JointId {
82 index1: recorded.index1,
83 world0: world.world_id,
84 generation: recorded.generation,
85 }
86 }
87
88 pub fn check_id(
89 ok: &mut bool,
90 kind: &str,
91 got_index: i32,
92 got_gen: u16,
93 rec_index: i32,
94 rec_gen: u16,
95 ) {
96 if got_index != rec_index || got_gen != rec_gen {
97 eprintln!(
98 "b3ReplayFile: {kind} id mismatch (rec index1={rec_index} gen={rec_gen}, got index1={got_index} gen={got_gen})"
99 );
100 *ok = false;
101 }
102 }
103}
104
105mod body_ops;
106mod joint_angular_type_ops;
107mod joint_linear_type_ops;
108mod joint_ops;
109mod query_ops;
110mod shape_ops;
111mod world_ops;
112
113pub fn dispatch_one(rdr: &mut RecReader<'_>) -> i32 {
115 if rdr.cursor >= rdr.size || !rdr.ok {
116 return -1;
117 }
118 let mut snap = rdr.snap();
119 let opcode = snap.u8();
120 let payload_size = snap.u24();
121 rdr.sync_from(&snap);
122 if !rdr.ok {
123 return -1;
124 }
125 let payload_start = rdr.cursor;
126
127 let Some(op) = RecOp::from_u8(opcode) else {
128 eprintln!("b3ReplayFile: unknown opcode 0x{opcode:02X}, skipping {payload_size} bytes");
129 if payload_size > (rdr.size - payload_start) as u32 {
130 rdr.ok = false;
131 } else {
132 rdr.cursor = payload_start + payload_size as i32;
133 }
134 return opcode as i32;
135 };
136
137 let handled = world_ops::dispatch(op, rdr, payload_start, payload_size)
138 || body_ops::dispatch(op, rdr, payload_start, payload_size)
139 || shape_ops::dispatch(op, rdr, payload_start, payload_size)
140 || joint_ops::dispatch(op, rdr, payload_start, payload_size)
141 || joint_linear_type_ops::dispatch(op, rdr, payload_start, payload_size)
142 || joint_angular_type_ops::dispatch(op, rdr, payload_start, payload_size)
143 || query_ops::dispatch(op, rdr, payload_start, payload_size);
144 debug_assert!(handled, "op {op:?} not claimed by any dispatch family");
145
146 opcode as i32
147}