some building fixes

This commit is contained in:
Robin Appelman 2022-08-27 14:38:33 +02:00
commit 37cc6dda99
4 changed files with 79 additions and 62 deletions

2
Cargo.lock generated
View file

@ -562,7 +562,7 @@ dependencies = [
[[package]]
name = "tf-demo-parser"
version = "0.4.0"
source = "git+https://github.com/demostf/parser#cb65c0d3ec07ad67587817e7475db5585e1b245f"
source = "git+https://github.com/demostf/parser#cf58a0609af6d135c09c8e0839a933e109938819"
dependencies = [
"bitbuffer",
"enumflags2",

View file

@ -8,6 +8,7 @@ export async function parseDemo(bytes: Uint8Array, progressCallback : (progress:
let buildingCount = state.building_count;
let boundaries = state.boundaries;
let interval_per_tick = state.interval_per_tick;
let tickCount = state.tick_count;
let kill_ticks = m.get_kill_ticks(state);
let attackers = m.get_attacker_ids(state);
let assisters = m.get_assister_ids(state);
@ -57,7 +58,8 @@ export async function parseDemo(bytes: Uint8Array, progressCallback : (progress:
},
data,
kills,
playerInfo
playerInfo,
tickCount,
);
}
@ -130,6 +132,7 @@ export interface BuildingState {
},
angle: number,
health: number,
level: number,
team: Team,
buildingType: BuildingType,
}
@ -167,7 +170,7 @@ export class ParsedDemo {
public readonly kills: Kill[];
public readonly playerInfo: PlayerInfo[];
constructor(playerCount: number, buildingCount: number, world: WorldBoundaries, header: Header, data: Uint8Array, kills: Kill[], playerInfo: PlayerInfo[]) {
constructor(playerCount: number, buildingCount: number, world: WorldBoundaries, header: Header, data: Uint8Array, kills: Kill[], playerInfo: PlayerInfo[], tickCount: number) {
this.playerCount = playerCount;
this.buildingCount = buildingCount;
this.world = world;
@ -175,7 +178,7 @@ export class ParsedDemo {
this.data = data;
this.kills = kills;
this.playerInfo = playerInfo;
this.tickCount = data.length / (playerCount * PLAYER_PACK_SIZE + buildingCount * BUILDING_PACK_SIZE);
this.tickCount = tickCount;
}
getPlayer(tick: number, playerIndex: number): PlayerState {
@ -189,10 +192,10 @@ export class ParsedDemo {
getBuilding(tick: number, buildingIndex: number): BuildingState {
if (buildingIndex >= this.buildingCount) {
throw new Error("Player out of bounds");
throw new Error("Building out of bounds");
}
const base = ((buildingIndex * this.tickCount) + tick) * BUILDING_PACK_SIZE;
const base = (this.playerCount * this.tickCount * PLAYER_PACK_SIZE) + ((buildingIndex * this.tickCount) + tick) * BUILDING_PACK_SIZE;
return unpackBuilding(this.data, base, this.world);
}
}
@ -227,7 +230,8 @@ function unpackBuilding(bytes: Uint8Array, base: number, world: WorldBoundaries)
const team_type_health = bytes[base + 4] + (bytes[base + 5] << 8);
const angle = unpack_angle(bytes[base + 6]);
const health = team_type_health & 1013;
const team = (team_type_health >> 13) as Team;
const team = (((team_type_health >> 13) & 1) === 0) ? Team.Blue : Team.Red;
const level = (team_type_health >> 14);
const buildingType = ((team_type_health >> 10) & 7) as BuildingType;
return {
@ -235,6 +239,7 @@ function unpackBuilding(bytes: Uint8Array, base: number, world: WorldBoundaries)
angle,
health,
team,
buildingType
buildingType,
level,
}
}

View file

@ -46,6 +46,7 @@ pub struct FlatState {
pub building_count: usize,
pub boundaries: WorldBoundaries,
pub interval_per_tick: f32,
pub tick_count: u32,
kill_ticks: Box<[u32]>,
attackers: Box<[u8]>,
assisters: Box<[u8]>,
@ -62,21 +63,24 @@ impl FlatState {
players,
header,
buildings,
max_building_count,
tick,
..
} = parsed;
let player_count = players.len();
let building_count = buildings.len();
let building_count = max_building_count;
let flat: Vec<_> = players
.into_iter()
.chain(buildings.into_iter())
.flat_map(|player| player.into_iter())
.flat_map(|data| data.into_iter())
.collect();
FlatState {
player_count,
building_count,
tick_count: tick as u32,
boundaries: world.into(),
interval_per_tick: header.duration / (header.ticks as f32),
data: flat.into_boxed_slice(),
@ -195,6 +199,8 @@ pub fn parse_demo_inner(
}
}
parsed_demo.finish();
let state = ticker.into_state();
parsed_demo.kills = state.kills;

View file

@ -1,7 +1,8 @@
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::parser::analyser::UserInfo;
use tf_demo_parser::demo::parser::gamestateanalyser::{
Building, Class, GameState, Kill, PlayerState as PlayerAliveState, Team, World,
Building, Class, Dispenser, GameState, Kill, PlayerState as PlayerAliveState, Sentry, Team,
Teleporter, World,
};
use tf_demo_parser::demo::vector::VectorXY;
@ -30,6 +31,7 @@ pub struct ParsedDemo {
pub kills: Vec<Kill>,
pub header: Header,
pub player_info: Vec<UserInfo>,
pub max_building_count: usize,
}
impl ParsedDemo {
@ -40,6 +42,7 @@ impl ParsedDemo {
buildings: Vec::new(),
kills: Vec::new(),
player_info: Vec::new(),
max_building_count: 0,
header,
}
}
@ -76,23 +79,32 @@ impl ParsedDemo {
let parsed_player = &mut self.players[index];
parsed_player.extend_from_slice(&state.pack(world));
}
for (index, building) in game_state.buildings.iter().enumerate() {
self.max_building_count = self.max_building_count.max(game_state.buildings.len());
for (index, building) in game_state.buildings.values().enumerate() {
let state = BuildingState::new(building);
if let None = self.buildings.get(index) {
let mut new_building =
let new_building =
Vec::with_capacity(self.header.ticks as usize * BuildingState::PACKET_SIZE);
new_building.resize(self.tick * BuildingState::PACKET_SIZE, 0);
self.buildings.push(new_building);
};
let parsed_player = &mut self.players[index];
parsed_player.extend_from_slice(&state.pack(world));
let parsed_building = &mut self.buildings[index];
parsed_building.resize(self.tick * BuildingState::PACKET_SIZE, 0);
parsed_building.extend_from_slice(&state.pack(world));
}
self.tick += 1;
}
}
pub fn finish(&mut self) {
for parsed_building in self.buildings.iter_mut() {
parsed_building.resize(self.tick * BuildingState::PACKET_SIZE, 0);
}
}
pub fn size(&self) -> usize {
self.players
.iter()
@ -255,29 +267,29 @@ impl BuildingType {
pub fn from_building(building: &Building) -> Self {
match building {
Building::Sentry { is_mini: true, .. } => BuildingType::MiniSentry,
Building::Sentry {
Building::Sentry(Sentry { is_mini: true, .. }) => BuildingType::MiniSentry,
Building::Sentry(Sentry {
is_mini: false,
level: 1,
..
} => BuildingType::Level1Sentry,
Building::Sentry {
}) => BuildingType::Level1Sentry,
Building::Sentry(Sentry {
is_mini: false,
level: 2,
..
} => BuildingType::Level2Sentry,
Building::Sentry {
}) => BuildingType::Level2Sentry,
Building::Sentry(Sentry {
is_mini: false,
level: 3,
..
} => BuildingType::Level3Sentry,
Building::Dispenser { .. } => BuildingType::Dispenser,
Building::Teleporter {
}) => BuildingType::Level3Sentry,
Building::Dispenser(Dispenser { .. }) => BuildingType::Dispenser,
Building::Teleporter(Teleporter {
is_entrance: true, ..
} => BuildingType::TeleporterEntrance,
Building::Teleporter {
}) => BuildingType::TeleporterEntrance,
Building::Teleporter(Teleporter {
is_entrance: false, ..
} => BuildingType::TeleporterExit,
}) => BuildingType::TeleporterExit,
_ => BuildingType::Unknown,
}
}
@ -290,43 +302,24 @@ pub struct BuildingState {
health: u16,
team: Team,
ty: BuildingType,
level: u8,
}
impl BuildingState {
const PACKET_SIZE: usize = 7;
pub fn new(building: &Building) -> Self {
match building {
Building::Sentry {
position,
angle,
health,
team,
..
}
| Building::Dispenser {
position,
angle,
health,
team,
..
}
| Building::Teleporter {
position,
angle,
health,
team,
..
} => BuildingState {
let position = building.position();
BuildingState {
position: VectorXY {
x: position.x,
y: position.y,
},
angle: Angle::from(*angle),
health: *health,
team: *team,
angle: Angle::from(building.angle()),
health: building.health(),
team: building.team(),
ty: BuildingType::from_building(building),
},
level: building.level(),
}
}
@ -341,11 +334,15 @@ impl BuildingState {
let x = pack_f32(self.position.x, world.boundary_min.x, world.boundary_max.x).to_le_bytes();
let y = pack_f32(self.position.y, world.boundary_min.y, world.boundary_max.y).to_le_bytes();
// 1 bits reserved
// 2 bits reserved
// 2 bits level
// 1 bit team
// 3 bits for type
// 10 bits for health
let team_type_health = ((self.team as u16) << 13) + ((self.ty as u16) << 10) + self.health;
let team = if self.team == Team::Blue { 0 } else { 1 };
let team_type_health = ((self.level as u16) << 14)
+ ((team as u16) << 13)
+ ((self.ty as u16) << 10)
+ self.health;
let combined_bytes = team_type_health.to_le_bytes();
[
@ -379,8 +376,14 @@ impl BuildingState {
let team_type_health = u16::from_le_bytes([bytes[4], bytes[5]]);
let health = team_type_health & 1023;
let angle = Angle(bytes[6]);
let team = Team::new(team_type_health >> 13);
let packed_team = (team_type_health >> 13) & 1;
let team = if packed_team == 0 {
Team::Blue
} else {
Team::Red
};
let ty = BuildingType::new((team_type_health >> 10) as u8 & 7);
let level = (team_type_health >> 14) as u8;
BuildingState {
position: VectorXY { x, y },
@ -388,6 +391,7 @@ impl BuildingState {
health,
team,
ty,
level,
}
}
}
@ -417,6 +421,7 @@ fn test_building_packing() {
angle: Angle::from(213.0),
health: 250,
team: Team::Blue,
level: 3,
ty: BuildingType::Level1Sentry,
};
@ -427,6 +432,7 @@ fn test_building_packing() {
assert_eq!(input.health, unpacked.health);
assert_eq!(input.ty, unpacked.ty);
assert_eq!(input.team, unpacked.team);
assert_eq!(input.level, unpacked.level);
assert!(f32::abs(input.position.x - unpacked.position.x) < 0.5);
assert!(f32::abs(input.position.y - unpacked.position.y) < 0.5);