mirror of
https://github.com/demostf/demo.js
synced 2026-06-03 16:44:12 +02:00
extract team and weapon data
This commit is contained in:
parent
6ce96e0e52
commit
5488b14e63
7 changed files with 200 additions and 41 deletions
|
|
@ -14,6 +14,8 @@ import {handleGameEvent} from "../PacketHandler/GameEvent";
|
||||||
import {handlePacketEntities} from "../PacketHandler/PacketEntities";
|
import {handlePacketEntities} from "../PacketHandler/PacketEntities";
|
||||||
import {handleGameEventList} from "../PacketHandler/GameEventList";
|
import {handleGameEventList} from "../PacketHandler/GameEventList";
|
||||||
import {handleDataTable} from "../PacketHandler/DataTable";
|
import {handleDataTable} from "../PacketHandler/DataTable";
|
||||||
|
import {Weapon} from "./Weapon";
|
||||||
|
import {Team} from "./Team";
|
||||||
|
|
||||||
export class Match {
|
export class Match {
|
||||||
tick: number;
|
tick: number;
|
||||||
|
|
@ -34,6 +36,10 @@ export class Match {
|
||||||
entityClasses: {[entityId: string]: ServerClass};
|
entityClasses: {[entityId: string]: ServerClass};
|
||||||
sendTableMap: {[name: string]: SendTable};
|
sendTableMap: {[name: string]: SendTable};
|
||||||
baseLineCache: {[serverClass: string]: PacketEntity};
|
baseLineCache: {[serverClass: string]: PacketEntity};
|
||||||
|
weaponMap: {[entityId: string]: Weapon};
|
||||||
|
outerMap: {[outer: number]: number};
|
||||||
|
teams: Team[];
|
||||||
|
teamMap: {[entityId: string]: Team};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tick = 0;
|
this.tick = 0;
|
||||||
|
|
@ -57,6 +63,10 @@ export class Match {
|
||||||
this.entityClasses = {};
|
this.entityClasses = {};
|
||||||
this.sendTableMap = {};
|
this.sendTableMap = {};
|
||||||
this.baseLineCache = {};
|
this.baseLineCache = {};
|
||||||
|
this.weaponMap = {};
|
||||||
|
this.outerMap = {};
|
||||||
|
this.teams = [];
|
||||||
|
this.teamMap = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
getSendTable(name) {
|
getSendTable(name) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class PacketEntity {
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('Property not found in entity');
|
throw new Error(`Property not found in entity (${originTable}.${name})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(): PacketEntity {
|
clone(): PacketEntity {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import {UserInfo} from "./UserInfo";
|
import {UserInfo} from "./UserInfo";
|
||||||
import {Vector} from "./Vector";
|
import {Vector} from "./Vector";
|
||||||
import {PlayerCondition} from "./PlayerCondition";
|
import {PlayerCondition} from "./PlayerCondition";
|
||||||
|
|
||||||
|
export enum LifeState {
|
||||||
|
ALIVE = 0,
|
||||||
|
DYING = 1,
|
||||||
|
DEATH = 2,
|
||||||
|
RESPAWNABLE = 3
|
||||||
|
}
|
||||||
|
|
||||||
export interface Player {
|
export interface Player {
|
||||||
user: UserInfo;
|
user: UserInfo;
|
||||||
position: Vector;
|
position: Vector;
|
||||||
|
|
@ -9,4 +17,7 @@ export interface Player {
|
||||||
classId: number;
|
classId: number;
|
||||||
team: number;
|
team: number;
|
||||||
viewAngle: number;
|
viewAngle: number;
|
||||||
|
weapons: number[];
|
||||||
|
ammo: number[];
|
||||||
|
lifeState: LifeState
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
src/Data/Team.ts
Normal file
7
src/Data/Team.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export interface Team {
|
||||||
|
teamNumber: number;
|
||||||
|
name: string;
|
||||||
|
score: number;
|
||||||
|
roundsWon: number;
|
||||||
|
players: number[];
|
||||||
|
}
|
||||||
13
src/Data/Weapon.ts
Normal file
13
src/Data/Weapon.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
export interface BaseCombatWeapon {
|
||||||
|
className: string;
|
||||||
|
owner: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CWeaponMedigun {
|
||||||
|
className: 'CWeaponMedigun';
|
||||||
|
healTarget: number;
|
||||||
|
chargeLevel: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Weapon = BaseCombatWeapon|
|
||||||
|
CWeaponMedigun;
|
||||||
|
|
@ -2,7 +2,8 @@ import {PacketEntitiesPacket} from "../Data/Packet";
|
||||||
import {Match} from "../Data/Match";
|
import {Match} from "../Data/Match";
|
||||||
import {PacketEntity, PVS} from "../Data/PacketEntity";
|
import {PacketEntity, PVS} from "../Data/PacketEntity";
|
||||||
import {Vector} from "../Data/Vector";
|
import {Vector} from "../Data/Vector";
|
||||||
import {Player} from "../Data/Player";
|
import {Player, LifeState} from "../Data/Player";
|
||||||
|
import {CWeaponMedigun, Weapon} from "../Data/Weapon";
|
||||||
|
|
||||||
export function handlePacketEntities(packet: PacketEntitiesPacket, match: Match) {
|
export function handlePacketEntities(packet: PacketEntitiesPacket, match: Match) {
|
||||||
for (const removedEntityId of packet.removedEntities) {
|
for (const removedEntityId of packet.removedEntities) {
|
||||||
|
|
@ -24,62 +25,179 @@ function saveEntity(packetEntity: PacketEntity, match: Match) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEntity(entity: PacketEntity, match: Match) {
|
function handleEntity(entity: PacketEntity, match: Match) {
|
||||||
|
for (const prop of entity.props) {
|
||||||
|
if (prop.definition.ownerTableName === 'DT_AttributeContainer' && prop.definition.name === 'm_hOuter') {
|
||||||
|
if (!match.outerMap[<number>prop.value]) {
|
||||||
|
match.outerMap[<number>prop.value] = entity.entityIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop of entity.props) {
|
||||||
|
if (prop.definition.ownerTableName === 'DT_BaseCombatWeapon' && prop.definition.name === 'm_hOwner') {
|
||||||
|
if (!match.weaponMap[entity.entityIndex]) {
|
||||||
|
match.weaponMap[entity.entityIndex] = {
|
||||||
|
className: entity.serverClass.name,
|
||||||
|
owner: <number>prop.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (entity.serverClass.name) {
|
switch (entity.serverClass.name) {
|
||||||
case 'CWorld':
|
case 'CWorld':
|
||||||
match.world.boundaryMin = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMins').value;
|
match.world.boundaryMin = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMins').value;
|
||||||
match.world.boundaryMax = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMaxs').value;
|
match.world.boundaryMax = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMaxs').value;
|
||||||
break;
|
break;
|
||||||
case 'CTFPlayer':
|
case 'CTFPlayer':
|
||||||
try {
|
/**
|
||||||
const player: Player = (match.playerMap[entity.entityIndex]) ? match.playerMap[entity.entityIndex] : {
|
"DT_TFPlayerScoringDataExclusive.m_iCaptures": 0,
|
||||||
user: match.getUserInfoForEntity(entity),
|
"DT_TFPlayerScoringDataExclusive.m_iDefenses": 0,
|
||||||
position: new Vector(0, 0, 0),
|
"DT_TFPlayerScoringDataExclusive.m_iKills": 5,
|
||||||
maxHealth: 0,
|
"DT_TFPlayerScoringDataExclusive.m_iDeaths": 17,
|
||||||
health: 0,
|
"DT_TFPlayerScoringDataExclusive.m_iSuicides": 7,
|
||||||
classId: 0,
|
"DT_TFPlayerScoringDataExclusive.m_iDominations": 0,
|
||||||
team: 0,
|
"DT_TFPlayerScoringDataExclusive.m_iRevenge": 0,
|
||||||
viewAngle: 0
|
"DT_TFPlayerScoringDataExclusive.m_iBuildingsBuilt": 0,
|
||||||
};
|
"DT_TFPlayerScoringDataExclusive.m_iBuildingsDestroyed": 0,
|
||||||
if (!match.playerMap[entity.entityIndex]) {
|
"DT_TFPlayerScoringDataExclusive.m_iHeadshots": 0,
|
||||||
match.playerMap[entity.entityIndex] = player;
|
"DT_TFPlayerScoringDataExclusive.m_iBackstabs": 0,
|
||||||
match.players.push(player);
|
"DT_TFPlayerScoringDataExclusive.m_iHealPoints": 0,
|
||||||
}
|
"DT_TFPlayerScoringDataExclusive.m_iInvulns": 0,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iTeleports": 0,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iDamageDone": 847,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iCrits": 0,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iResupplyPoints": 0,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iKillAssists": 0,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iBonusPoints": 0,
|
||||||
|
"DT_TFPlayerScoringDataExclusive.m_iPoints": 6,
|
||||||
|
"DT_TFPlayerSharedLocal.m_nDesiredDisguiseTeam": 0,
|
||||||
|
"DT_TFPlayerSharedLocal.m_nDesiredDisguiseClass": 0,
|
||||||
|
"DT_TFPlayerShared.m_iKillStreak": 0,
|
||||||
|
"DT_BaseCombatCharacter.m_hActiveWeapon": 2097151,
|
||||||
|
"DT_TFPlayerShared.m_flCloakMeter": 100,
|
||||||
|
*/
|
||||||
|
|
||||||
|
const player: Player = (match.playerMap[entity.entityIndex]) ? match.playerMap[entity.entityIndex] : {
|
||||||
|
user: match.getUserInfoForEntity(entity),
|
||||||
|
position: new Vector(0, 0, 0),
|
||||||
|
maxHealth: 0,
|
||||||
|
health: 0,
|
||||||
|
classId: 0,
|
||||||
|
team: 0,
|
||||||
|
viewAngle: 0,
|
||||||
|
weapons: [],
|
||||||
|
ammo: [],
|
||||||
|
lifeState: LifeState.DEATH
|
||||||
|
};
|
||||||
|
if (!match.playerMap[entity.entityIndex]) {
|
||||||
|
match.playerMap[entity.entityIndex] = player;
|
||||||
|
match.players.push(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop of entity.props) {
|
||||||
|
if (prop.definition.ownerTableName === 'm_hMyWeapons') {
|
||||||
|
if (prop.value !== 2097151) {
|
||||||
|
player.weapons[parseInt(prop.definition.name, 10)] = <number>prop.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prop.definition.ownerTableName === 'm_iAmmo') {
|
||||||
|
if (prop.value > 0) {
|
||||||
|
player.ammo[parseInt(prop.definition.name, 10)] = <number>prop.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const propName = prop.definition.ownerTableName + '.' + prop.definition.name;
|
||||||
|
switch (propName) {
|
||||||
|
case 'DT_BasePlayer.m_iHealth':
|
||||||
|
player.health = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_BasePlayer.m_iMaxHealth':
|
||||||
|
player.maxHealth = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_TFLocalPlayerExclusive.m_vecOrigin':
|
||||||
|
player.position.x = (<Vector>prop.value).x;
|
||||||
|
player.position.y = (<Vector>prop.value).y;
|
||||||
|
break;
|
||||||
|
case 'DT_TFNonLocalPlayerExclusive.m_vecOrigin':
|
||||||
|
player.position.x = (<Vector>prop.value).x;
|
||||||
|
player.position.y = (<Vector>prop.value).y;
|
||||||
|
break;
|
||||||
|
case 'DT_TFLocalPlayerExclusive.m_vecOrigin[2]':
|
||||||
|
player.position.z = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_TFNonLocalPlayerExclusive.m_vecOrigin[2]':
|
||||||
|
player.position.z = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_TFNonLocalPlayerExclusive.m_angEyeAngles[1]':
|
||||||
|
player.viewAngle = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_TFLocalPlayerExclusive.m_angEyeAngles[1]':
|
||||||
|
player.viewAngle = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_BasePlayer.m_lifeState':
|
||||||
|
player.lifeState = <number>prop.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'CWeaponMedigun':
|
||||||
|
const weapon = <CWeaponMedigun>match.weaponMap[entity.entityIndex];
|
||||||
|
for (const prop of entity.props) {
|
||||||
|
const propName = prop.definition.ownerTableName + '.' + prop.definition.name;
|
||||||
|
switch (propName) {
|
||||||
|
case 'DT_WeaponMedigun.m_hHealingTarget':
|
||||||
|
weapon.healTarget = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_TFWeaponMedigunDataNonLocal.m_flChargeLevel':
|
||||||
|
weapon.chargeLevel = <number>prop.value;
|
||||||
|
break;
|
||||||
|
case 'DT_LocalTFWeaponMedigunData.m_flChargeLevel':
|
||||||
|
weapon.chargeLevel = <number>prop.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case'CTFTeam':
|
||||||
|
try {
|
||||||
|
const teamId = <number>entity.getProperty('DT_Team', 'm_iTeamNum').value;
|
||||||
|
if (!match.teams[teamId]) {
|
||||||
|
match.teams[teamId] = {
|
||||||
|
name: <string>entity.getProperty('DT_Team', 'm_szTeamname').value,
|
||||||
|
score: <number>entity.getProperty('DT_Team', 'm_iScore').value,
|
||||||
|
roundsWon: <number>entity.getProperty('DT_Team', 'm_iRoundsWon').value,
|
||||||
|
players: <number[]>entity.getProperty('DT_Team', '"player_array"').value,
|
||||||
|
teamNumber: <number>teamId
|
||||||
|
};
|
||||||
|
match.teamMap[entity.entityIndex] = match.teams[teamId];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
const team = match.teamMap[entity.entityIndex];
|
||||||
for (const prop of entity.props) {
|
for (const prop of entity.props) {
|
||||||
const propName = prop.definition.ownerTableName + '.' + prop.definition.name;
|
const propName = prop.definition.ownerTableName + '.' + prop.definition.name;
|
||||||
switch (propName) {
|
switch (propName) {
|
||||||
case 'DT_BasePlayer.m_iHealth':
|
case 'DT_Team.m_iScore':
|
||||||
player.health = <number>prop.value;
|
team.score = <number>prop.value;
|
||||||
break;
|
break;
|
||||||
case 'DT_BasePlayer.m_iMaxHealth':
|
case 'DT_Team.m_szTeamname':
|
||||||
player.maxHealth = <number>prop.value;
|
team.name = <string>prop.value;
|
||||||
break;
|
break;
|
||||||
case 'DT_TFLocalPlayerExclusive.m_vecOrigin':
|
case 'DT_Team.m_iRoundsWon':
|
||||||
player.position.x = (<Vector>prop.value).x;
|
team.roundsWon = <number>prop.value;
|
||||||
player.position.y = (<Vector>prop.value).y;
|
|
||||||
break;
|
break;
|
||||||
case 'DT_TFNonLocalPlayerExclusive.m_vecOrigin':
|
case 'DT_Team."player_array"':
|
||||||
player.position.x = (<Vector>prop.value).x;
|
team.players = <number[]>prop.value;
|
||||||
player.position.y = (<Vector>prop.value).y;
|
|
||||||
break;
|
|
||||||
case 'DT_TFLocalPlayerExclusive.m_vecOrigin[2]':
|
|
||||||
player.position.z = <number>prop.value;
|
|
||||||
break;
|
|
||||||
case 'DT_TFNonLocalPlayerExclusive.m_vecOrigin[2]':
|
|
||||||
player.position.z = <number>prop.value;
|
|
||||||
break;
|
|
||||||
case 'DT_TFNonLocalPlayerExclusive.m_angEyeAngles[1]':
|
|
||||||
player.viewAngle = <number>prop.value;
|
|
||||||
break;
|
|
||||||
case 'DT_TFLocalPlayerExclusive.m_angEyeAngles[1]':
|
|
||||||
player.viewAngle = <number>prop.value;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// process.exit();
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 'CTFPlayerResource':
|
||||||
|
break;
|
||||||
|
case 'CObjectSentrygun':
|
||||||
|
break;
|
||||||
|
case 'CTeamRoundTimer':
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export class SendPropParser {
|
||||||
if (value instanceof Array) {
|
if (value instanceof Array) {
|
||||||
throw new Error('Nested arrays not supported');
|
throw new Error('Nested arrays not supported');
|
||||||
}
|
}
|
||||||
values.push();
|
values.push(value);
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue