mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
split out updating match state from packet
This commit is contained in:
parent
cb02fdee9b
commit
085887d31d
5 changed files with 163 additions and 134 deletions
|
|
@ -7,10 +7,13 @@ import {GameEventDefinitionMap} from "./GameEvent";
|
||||||
import {BitStream} from "bit-buffer";
|
import {BitStream} from "bit-buffer";
|
||||||
import {UserInfo} from "./UserInfo";
|
import {UserInfo} from "./UserInfo";
|
||||||
import {World} from "./World";
|
import {World} from "./World";
|
||||||
import {Vector} from "./Vector";
|
|
||||||
import {Player} from "./Player";
|
import {Player} from "./Player";
|
||||||
import {Death} from "./Death";
|
import {Death} from "./Death";
|
||||||
import {HandlerMap} from "../PacketHandler/Handler";
|
import {handleStringTable} from "../PacketHandler/StringTable";
|
||||||
|
import {handleSayText2} from "../PacketHandler/SayText2";
|
||||||
|
import {handleGameEvent} from "../PacketHandler/GameEvent";
|
||||||
|
import {handlePacketEntities} from "../PacketHandler/PacketEntities";
|
||||||
|
|
||||||
export class Match {
|
export class Match {
|
||||||
tick: number;
|
tick: number;
|
||||||
chat: any[];
|
chat: any[];
|
||||||
|
|
@ -86,11 +89,7 @@ export class Match {
|
||||||
handlePacket(packet) {
|
handlePacket(packet) {
|
||||||
switch (packet.packetType) {
|
switch (packet.packetType) {
|
||||||
case 'packetEntities':
|
case 'packetEntities':
|
||||||
for (const entity of this.entities) {
|
handlePacketEntities(packet, this);
|
||||||
if (entity) {
|
|
||||||
this.handleEntity(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'netTick':
|
case 'netTick':
|
||||||
if (this.startTick === 0) {
|
if (this.startTick === 0) {
|
||||||
|
|
@ -102,82 +101,13 @@ export class Match {
|
||||||
this.intervalPerTick = packet.intervalPerTick;
|
this.intervalPerTick = packet.intervalPerTick;
|
||||||
break;
|
break;
|
||||||
case 'sayText2':
|
case 'sayText2':
|
||||||
this.chat.push({
|
handleSayText2(packet, this);
|
||||||
kind: packet.kind,
|
|
||||||
from: packet.from,
|
|
||||||
text: packet.text,
|
|
||||||
tick: this.tick
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'stringTable':
|
case 'stringTable':
|
||||||
for (const table of <StringTable[]>packet.tables) {
|
handleStringTable(packet, this);
|
||||||
if (table.name === 'userinfo') {
|
|
||||||
for (const userData of table.entries) {
|
|
||||||
if (userData.extraData) {
|
|
||||||
if (userData.extraData.bitsLeft > (32 * 8)) {
|
|
||||||
const name = userData.extraData.readUTF8String(32);
|
|
||||||
const userId = userData.extraData.readUint32();
|
|
||||||
const steamId = userData.extraData.readUTF8String();
|
|
||||||
if (steamId) {
|
|
||||||
const userState = this.getUserInfo(userId);
|
|
||||||
userState.name = name;
|
|
||||||
userState.steamId = steamId;
|
|
||||||
userState.entityId = parseInt(userData.text, 10) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'gameEvent':
|
case 'gameEvent':
|
||||||
switch (packet.event.name) {
|
handleGameEvent(packet, this);
|
||||||
case 'player_death':
|
|
||||||
while (packet.event.values.assister > 256 && packet.event.values.assister < (1024 * 16)) {
|
|
||||||
packet.event.values.assister -= 256;
|
|
||||||
}
|
|
||||||
const assister = packet.event.values.assister < 256 ? packet.event.values.assister : null;
|
|
||||||
// todo get player names, not same id as the name string table (entity id)
|
|
||||||
while (packet.event.values.attacker > 256) {
|
|
||||||
packet.event.values.attacker -= 256;
|
|
||||||
}
|
|
||||||
while (packet.event.values.userid > 256) {
|
|
||||||
packet.event.values.userid -= 256;
|
|
||||||
}
|
|
||||||
this.deaths.push({
|
|
||||||
killer: packet.event.values.attacker,
|
|
||||||
assister: assister,
|
|
||||||
victim: packet.event.values.userid,
|
|
||||||
weapon: packet.event.values.weapon,
|
|
||||||
tick: this.tick
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'teamplay_round_win':
|
|
||||||
if (packet.event.values.winreason !== 6) {// 6 = timelimit
|
|
||||||
this.rounds.push({
|
|
||||||
winner: packet.event.values.team === 2 ? 'red' : 'blue',
|
|
||||||
length: packet.event.values.round_time,
|
|
||||||
end_tick: this.tick
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'player_spawn':
|
|
||||||
const userId = packet.event.values.userid;
|
|
||||||
const userState = this.getUserInfo(userId);
|
|
||||||
const player = this.playerMap[userState.entityId];
|
|
||||||
userState.team = packet.event.values.team === 2 ? 'red' : 'blue';
|
|
||||||
const classId = packet.event.values.class;
|
|
||||||
if (player) {
|
|
||||||
player.classId = classId;
|
|
||||||
player.team = packet.event.values.team;
|
|
||||||
}
|
|
||||||
if (!userState.classes[classId]) {
|
|
||||||
userState.classes[classId] = 0;
|
|
||||||
}
|
|
||||||
userState.classes[classId]++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -222,59 +152,4 @@ export class Match {
|
||||||
get classBits() {
|
get classBits() {
|
||||||
return Math.ceil(Math.log(this.serverClasses.length) * Math.LOG2E)
|
return Math.ceil(Math.log(this.serverClasses.length) * Math.LOG2E)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEntity(entity: Entity) {
|
|
||||||
switch (entity.serverClass.name) {
|
|
||||||
case 'CWorld':
|
|
||||||
this.world.boundaryMin = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMins').value;
|
|
||||||
this.world.boundaryMax = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMaxs').value;
|
|
||||||
break;
|
|
||||||
case 'CTFPlayer':
|
|
||||||
try {
|
|
||||||
const player: Player = (this.playerMap[entity.entityIndex]) ? this.playerMap[entity.entityIndex] : {
|
|
||||||
user: this.getUserInfoForEntity(entity),
|
|
||||||
position: new Vector(0, 0, 0),
|
|
||||||
maxHealth: 0,
|
|
||||||
health: 0,
|
|
||||||
classId: 0,
|
|
||||||
team: 0
|
|
||||||
};
|
|
||||||
if (!this.playerMap[entity.entityIndex]) {
|
|
||||||
this.playerMap[entity.entityIndex] = player;
|
|
||||||
this.players.push(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const prop of entity.updatedProps) {
|
|
||||||
const propName = prop.definition.ownerTableName + '.' + prop.definition.name;
|
|
||||||
// console.log(propName, prop.value);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
entity.updatedProps = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
src/PacketHandler/GameEvent.ts
Normal file
51
src/PacketHandler/GameEvent.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {GameEventPacket} from "../Data/Packet";
|
||||||
|
import {Match} from "../Data/Match";
|
||||||
|
|
||||||
|
export function handleGameEvent(packet: GameEventPacket, match: Match) {
|
||||||
|
switch (packet.event.name) {
|
||||||
|
case 'player_death':
|
||||||
|
while (packet.event.values.assister > 256 && packet.event.values.assister < (1024 * 16)) {
|
||||||
|
packet.event.values.assister -= 256;
|
||||||
|
}
|
||||||
|
const assister = packet.event.values.assister < 256 ? packet.event.values.assister : null;
|
||||||
|
// todo get player names, not same id as the name string table (entity id)
|
||||||
|
while (packet.event.values.attacker > 256) {
|
||||||
|
packet.event.values.attacker -= 256;
|
||||||
|
}
|
||||||
|
while (packet.event.values.userid > 256) {
|
||||||
|
packet.event.values.userid -= 256;
|
||||||
|
}
|
||||||
|
match.deaths.push({
|
||||||
|
killer: packet.event.values.attacker,
|
||||||
|
assister: assister,
|
||||||
|
victim: packet.event.values.userid,
|
||||||
|
weapon: packet.event.values.weapon,
|
||||||
|
tick: match.tick
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'teamplay_round_win':
|
||||||
|
if (packet.event.values.winreason !== 6) {// 6 = timelimit
|
||||||
|
match.rounds.push({
|
||||||
|
winner: packet.event.values.team === 2 ? 'red' : 'blue',
|
||||||
|
length: packet.event.values.round_time,
|
||||||
|
end_tick: match.tick
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'player_spawn':
|
||||||
|
const userId = packet.event.values.userid;
|
||||||
|
const userState = match.getUserInfo(userId);
|
||||||
|
const player = match.playerMap[userState.entityId];
|
||||||
|
userState.team = packet.event.values.team === 2 ? 'red' : 'blue';
|
||||||
|
const classId = packet.event.values.class;
|
||||||
|
if (player) {
|
||||||
|
player.classId = classId;
|
||||||
|
player.team = packet.event.values.team;
|
||||||
|
}
|
||||||
|
if (!userState.classes[classId]) {
|
||||||
|
userState.classes[classId] = 0;
|
||||||
|
}
|
||||||
|
userState.classes[classId]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/PacketHandler/PacketEntities.ts
Normal file
68
src/PacketHandler/PacketEntities.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import {PacketEntitiesPacket} from "../Data/Packet";
|
||||||
|
import {Match} from "../Data/Match";
|
||||||
|
import {Entity} from "../Data/Entity";
|
||||||
|
import {Vector} from "../Data/Vector";
|
||||||
|
import {Player} from "../Data/Player";
|
||||||
|
|
||||||
|
export function handlePacketEntities(packet: PacketEntitiesPacket, match: Match) {
|
||||||
|
for (const entity of match.entities) {
|
||||||
|
if (entity) {
|
||||||
|
handleEntity(entity, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEntity(entity: Entity, match: Match) {
|
||||||
|
switch (entity.serverClass.name) {
|
||||||
|
case 'CWorld':
|
||||||
|
match.world.boundaryMin = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMins').value;
|
||||||
|
match.world.boundaryMax = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMaxs').value;
|
||||||
|
break;
|
||||||
|
case 'CTFPlayer':
|
||||||
|
try {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
if (!match.playerMap[entity.entityIndex]) {
|
||||||
|
match.playerMap[entity.entityIndex] = player;
|
||||||
|
match.players.push(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop of entity.updatedProps) {
|
||||||
|
const propName = prop.definition.ownerTableName + '.' + prop.definition.name;
|
||||||
|
// console.log(propName, prop.value);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
entity.updatedProps = [];
|
||||||
|
}
|
||||||
11
src/PacketHandler/SayText2.ts
Normal file
11
src/PacketHandler/SayText2.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {SayText2Packet} from "../Data/Packet";
|
||||||
|
import {Match} from "../Data/Match";
|
||||||
|
|
||||||
|
export function handleSayText2(packet: SayText2Packet, match: Match) {
|
||||||
|
match.chat.push({
|
||||||
|
kind: packet.kind,
|
||||||
|
from: packet.from,
|
||||||
|
text: packet.text,
|
||||||
|
tick: match.tick
|
||||||
|
});
|
||||||
|
}
|
||||||
24
src/PacketHandler/StringTable.ts
Normal file
24
src/PacketHandler/StringTable.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import {StringTablePacket} from "../Data/Packet";
|
||||||
|
import {Match} from "../Data/Match";
|
||||||
|
|
||||||
|
export function handleStringTable(packet: StringTablePacket, match: Match) {
|
||||||
|
for (const table of packet.tables) {
|
||||||
|
if (table.name === 'userinfo') {
|
||||||
|
for (const userData of table.entries) {
|
||||||
|
if (userData.extraData) {
|
||||||
|
if (userData.extraData.bitsLeft > (32 * 8)) {
|
||||||
|
const name = userData.extraData.readUTF8String(32);
|
||||||
|
const userId = userData.extraData.readUint32();
|
||||||
|
const steamId = userData.extraData.readUTF8String();
|
||||||
|
if (steamId) {
|
||||||
|
const userState = match.getUserInfo(userId);
|
||||||
|
userState.name = name;
|
||||||
|
userState.steamId = steamId;
|
||||||
|
userState.entityId = parseInt(userData.text, 10) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue