urt-rs / crates /urt-gameplay /src /bin /urt-client-processing.rs
nmarshall's picture
gameplay: compose pre-movement client policies
99a6f51
Raw History Blame Contribute Delete
8.08 kB
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::<Vec<_>>()
.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::<Vec<_>>()
.join(",")
}
fn main() {
let mut args = std::env::args().skip(1);
let kind = args.next().unwrap();
let w: Vec<u32> = 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<u32>) -> String {
v.map(|v| v.to_string()).unwrap_or("null".into())
}
fn optional_bool(v: Option<bool>) -> 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]
)
}