use urt_gameplay::client_processing::*; fn hex(v: &[u8]) -> String { v.iter().map(|v| format!("{v:02x}")).collect() } fn list(v: &[u32]) -> String { format!( "[{}]", v.iter() .map(|v| v.to_string()) .collect::>() .join(",") ) } fn events(v: &[ProcessingEffect]) -> String { v.iter() .map(|e| match e { ProcessingEffect::Intermission { slot } => { format!(r#"{{"op":"intermission","slot":{}}}"#, slot) } ProcessingEffect::Spectator { slot, command_time } => format!( r#"{{"op":"spectator","slot":{},"command_time":{}}}"#, slot, command_time ), ProcessingEffect::Team { slot, text, force } => format!( r#"{{"op":"team","slot":{},"text":"{}","force":{}}}"#, slot, hex(text), force ), ProcessingEffect::Send { slot, text } => { format!(r#"{{"op":"send","slot":{},"text":"{}"}}"#, slot, hex(text)) } ProcessingEffect::Drop { slot, text } => { format!(r#"{{"op":"drop","slot":{},"text":"{}"}}"#, slot, hex(text)) } }) .collect::>() .join(",") } fn main() { let mut args = std::env::args().skip(1); let kind = args.next().unwrap(); let w: Vec = args.map(|v| v.parse().unwrap()).collect(); match kind.as_str() { "route" => { assert_eq!(w.len(), 4); let out = route_client(RoutingInput { intermission: w[0], team: w[1], follow: w[2], spectator: w[3], slot: 2, command_time: 12345, }) .unwrap(); println!( r#"{{"events":[{}],"proceed":{}}}"#, events(&out.effects), out.route == ClientRoute::Continue ); } "command" => { assert_eq!(w.len(), 13); let mut state = CommandState { angles: w[4..7].try_into().unwrap(), saved: w[7..10].try_into().unwrap(), buttons: w[10], move_word: w[11], }; rewrite_client_command( &mut state, CommandControl { pause: w[0], mode: w[1], flags: w[2], block: w[3], }, ); println!( r#"{{"angles":{},"saved":{},"buttons":{},"move_word":{},"command_time":{}}}"#, list(&state.angles), list(&state.saved), state.buttons, state.move_word, w[12] ); } "idle" => { assert_eq!(w.len(), 16); let mut state = InactivityState { deadline: w[12], warned: w[13], }; let out = client_inactivity( &mut state, InactivityInput { entity_flags: w[0], idle_seconds: w[1], pause: w[2], pause_anchor: w[3], tick: w[4], moves: [w[5] as u8, w[6] as u8, w[7] as u8], buttons: w[8], health: w[9], exempt: w[10], mode: w[11], msec: w[14], slot: 2, }, ) .unwrap(); assert_eq!(w[15], 2); println!( r#"{{"accepted":{},"events":[{}],"deadline":{},"warned":{},"health":{}}}"#, out.accepted, events(&out.effects), state.deadline, state.warned, w[9] ); } "movement" => { assert_eq!(w.len(), 11); let mut state = MovementState { pm_type: 99, gravity: 111, speed: 222, controller: w[3] != 0, packed: [333, 444], event_sequence: w[10], }; let seq = setup_client_movement( &mut state, MovementInput { pause: w[0], noclip: w[1], health: w[2], gravity: w[4], factor: w[5], packs: w[6..10].try_into().unwrap(), }, ) .unwrap(); println!( r#"{{"pm_type":{},"gravity_word":{},"speed":{},"controller":{},"packed":{},"event_sequence":{}}}"#, state.pm_type, state.gravity, state.speed, u8::from(state.controller), list(&state.packed), seq ); } "pipeline" => println!("{}", pipeline(&w)), _ => panic!("unknown policy"), } } fn optional(v: Option) -> String { v.map(|v| v.to_string()).unwrap_or("null".into()) } fn optional_bool(v: Option) -> String { v.map(|v| v.to_string()).unwrap_or("null".into()) } fn pipeline(w: &[u32]) -> String { assert_eq!(w.len(), 38); let mut s = ClientProcessingState { timing: urt_gameplay::client_timer::TimerPrepareState { connection: w[0], exception: w[1], command_time: w[2], current_time: w[3], quantize: w[4], copied_tick: 17, copied_command: 18, }, command: CommandState { angles: w[27..30].try_into().unwrap(), saved: w[30..33].try_into().unwrap(), buttons: w[25], move_word: w[26], }, inactivity: InactivityState { deadline: w[23], warned: w[24], }, movement: MovementState { pm_type: 99, gravity: 111, speed: 222, controller: w[20] != 0, packed: [333, 444], event_sequence: w[37], }, }; let input = ClientProcessingInput { slot: 2, tick: w[5], intermission: w[6], team: w[7], follow: w[8], pause: w[9], game_mode: w[10], client_flags: w[11], block: w[12], entity_flags: w[13], idle_seconds: w[14], pause_anchor: w[15], health: w[16], exempt: w[17], idle_mode: w[18], noclip: w[19], gravity: w[21], factor: w[22], packs: w[33..37].try_into().unwrap(), }; let out = client_pre_movement(&mut s, input).unwrap(); let command = format!( r#"{{"angles":{},"saved":{},"buttons":{},"move_word":{},"command_time":{}}}"#, list(&s.command.angles), list(&s.command.saved), s.command.buttons, s.command.move_word, s.timing.command_time ); let movement = format!( r#"{{"pm_type":{},"gravity_word":{},"speed":{},"controller":{},"packed":{},"event_sequence":{}}}"#, s.movement.pm_type, s.movement.gravity, s.movement.speed, u8::from(s.movement.controller), list(&s.movement.packed), optional(out.event_sequence) ); format!( r#"{{"proceed":{},"events":[{}],"msec":{},"idle_result":{},"idle_msec":{},"timing":{},"command":{},"idle_state":{},"movement":{},"health":{}}}"#, out.proceed, events(&out.effects), optional(out.msec), optional_bool(out.idle_result), optional(out.idle_msec), list(&[ s.timing.command_time, s.timing.current_time, s.timing.copied_tick, s.timing.copied_command ]), command, list(&[s.inactivity.deadline, s.inactivity.warned]), movement, w[16] ) }