mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 10:04:12 +02:00
some building fixes
This commit is contained in:
parent
23c0f49a8c
commit
37cc6dda99
4 changed files with 79 additions and 62 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
19
js/parser.ts
19
js/parser.ts
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/lib.rs
10
src/lib.rs
|
|
@ -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;
|
||||
|
|
|
|||
110
src/state.rs
110
src/state.rs
|
|
@ -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 {
|
||||
position: VectorXY {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
},
|
||||
angle: Angle::from(*angle),
|
||||
health: *health,
|
||||
team: *team,
|
||||
ty: BuildingType::from_building(building),
|
||||
let position = building.position();
|
||||
BuildingState {
|
||||
position: VectorXY {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
},
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue