usercmd button

This commit is contained in:
Robin Appelman 2021-08-01 17:37:50 +02:00
commit 69d1318a43
3 changed files with 173 additions and 120 deletions

View file

@ -0,0 +1,94 @@
import {GameEventDefinition, Message, PacketEntity, SendPropValue} from "../parser";
import React from "react";
export interface MessageInfoProps {
msg: Message,
prop_names: Map<number, { table: String, prop: String }>,
class_names: Map<number, String>
}
export function MessageInfo({msg, prop_names, class_names}: MessageInfoProps) {
switch (msg.type) {
case "Print":
return <>{msg.value}</>
case "ServerInfo":
return <>stv: {msg.stv ? 'true' : 'false'}, map: {msg.map}, player count: {msg.player_count},
map: {msg.map}</>
case "NetTick":
return <>Tick {msg.tick}, frame time: {msg.frame_time}, std_dev: {msg.std_dev}</>
case "ParseSounds":
return <>{msg.reliable ? 'reliable' : 'unreliable'} {msg.num} sounds: {msg.length} bits</>
case "VoiceInit":
return <>{msg.codec} at quality {msg.quality} and sampling rage {msg.sampling_rate}</>
case "SigOnState":
return <>state: {msg.state}, count: {msg.count}</>
case "SetConVar":
return <>{msg.vars.map(cvar => `${cvar.key}=${cvar.value}`).join(', ')}</>
case "SetView":
return <>set view to entity {msg.index}</>
case "GameEventList":
return <>{msg.event_list.map(formatEventDefinition).map((str, i) => (<p key={i}>{str}</p>))}</>
case "PacketEntities":
let entities = msg.entities.map(entity => formatEntity(entity, prop_names, class_names)).map((str, i) => <p
key={i}>{str}</p>);
let deleted = <></>
if (msg.removed_entities.length > 0) {
deleted = <>deleted: {msg.removed_entities.join(', ')}</>
}
return <>
<p>delta: {JSON.stringify(msg.delta)}</p>
<p>baseline: {JSON.stringify(msg.base_line)}</p>
<p>max: {JSON.stringify(msg.max_entries)}</p>
<p>updated base line: {JSON.stringify(msg.updated_base_line)}</p>
{entities}
{deleted}
</>
case "TempEntities":
let events = msg.events.map(event => {
let class_name = class_names.get(event.class_id);
let props = event.props.map(prop => {
let names = prop_names.get(prop.identifier);
if (names) {
return `${names.table}.${names.prop}=${formatPropValue(prop.value)}`;
} else {
return `[unknown prop]=${prop.value}`;
}
});
return `temp entity ${class_name}(delay: ${event.fire_delay}, reliable:${JSON.stringify(event.reliable)}): ` + props.join(', ');
})
return <>{events.map(event => <p>{event}</p>)}</>
default:
let json = msg;
delete json.type;
return <>{JSON.stringify(json)}</>
}
}
function formatPropValue(value: SendPropValue): string {
if (Array.isArray(value)) {
return '[' + value.map(formatPropValue).join(',') + ']'
} else if (typeof value === "number" || typeof value === "string") {
return `${value}`
} else {
return JSON.stringify(value)
}
}
function formatEntity(entity: PacketEntity, prop_names: Map<number, { table: String, prop: String }>, class_names: Map<number, String>,): string {
let class_name = class_names.get(entity.server_class);
let props = entity.props.map(prop => {
let names = prop_names.get(prop.identifier);
if (names) {
return `${names.table}.${names.prop}=${formatPropValue(prop.value)}`;
} else {
return `[unknown prop]=${prop.value}`;
}
})
return `entity ${entity.entity_index}(${class_name}) ${entity.pvs}: ` + props.join(', ');
}
function formatEventDefinition(event: GameEventDefinition): string {
let values = event.entries.map(entry => `${entry.name}: ${entry.kind}`);
return `${event.event_type}{${values.join(', ')}}`;
}

View file

@ -0,0 +1,75 @@
import {UserCmd} from "../parser";
import React from "react";
enum Button {
ATTACK = (1 << 0),
JUMP = (1 << 1),
DUCK = (1 << 2),
FORWARD = (1 << 3),
BACK = (1 << 4),
USE = (1 << 5),
CANCEL = (1 << 6),
LEFT = (1 << 7),
RIGHT = (1 << 8),
MOVELEFT = (1 << 9),
MOVERIGHT = (1 << 10),
ATTACK2 = (1 << 11),
RUN = (1 << 12),
RELOAD = (1 << 13),
ALT1 = (1 << 14),
ALT2 = (1 << 15),
SCORE = (1 << 16),
SPEED = (1 << 17),
WALK = (1 << 18),
ZOOM = (1 << 19),
WEAPON1 = (1 << 20),
WEAPON2 = (1 << 21),
BULLRUSH = (1 << 22),
GRENADE1 = (1 << 23),
GRENADE2 = (1 << 24),
ATTACK3 = (1 << 25),
}
namespace Button {
export function toValues(n: Button) {
const values: string[] = [];
while (n) {
const bit = n & (~n+1);
values.push(Button[bit]);
n ^= bit;
}
return values;
}
}
export function UserCmdDetails({cmd}: { cmd: UserCmd }) {
let out = `${cmd.command_number} - tick ${cmd.tick_count}: `;
const formatOptionNum = (x: number | null) => x === null ? 0 : x;
const anyNonNull = (xs: (number | null)[]) => xs.findIndex(x => x !== null) != -1;
let parts = [];
if (anyNonNull(cmd.view_angles)) {
parts.push(`view angles: ${cmd.view_angles.map(formatOptionNum).join(',')}`);
}
if (anyNonNull(cmd.movement)) {
parts.push(`movement: ${cmd.movement.map(formatOptionNum).join(',')}`);
}
if (cmd.mouse_dx !== null || cmd.mouse_dy !== null) {
parts.push(`mouse: ${[cmd.mouse_dx, cmd.mouse_dy].map(formatOptionNum).join(',')}`);
}
if (cmd.impulse) {
parts.push(`impulse ${cmd.impulse}`)
}
if (cmd.buttons) {
parts.push(`buttons ${Button.toValues(cmd.buttons as Button)}`)
}
if (cmd.weapon_select) {
parts.push(`weapon ${cmd.weapon_select.select}(${cmd.weapon_select.subtype})`)
}
if (parts.length == 0) {
parts.push(`no data`)
}
return <>{out + parts.join(', ')}</>
}

View file

@ -1,6 +1,8 @@
import React, {CSSProperties, JSXElementConstructor, ReactElement} from 'react';
import {GameEventDefinition, Message, Packet, PacketEntity, SendPropValue, UserCmd} from "./parser";
import {FixedSizeList as List} from 'react-window';
import {MessageInfo} from "./packets/message";
import {UserCmdDetails} from "./packets/usercmd";
interface TableProps {
packets: Packet[],
@ -85,7 +87,7 @@ export function PacketDetails({packet, prop_names, class_names}: DetailProps) {
case "Message":
let rows = packet.messages.map((message, y) => <tr key={y}>
<td className="type">{message.type}</td>
<td>{messageInfoText(message, prop_names, class_names)}</td>
<td><MessageInfo msg={message} prop_names={prop_names} class_names={class_names}/></td>
</tr>)
return (
<table>
@ -99,7 +101,7 @@ export function PacketDetails({packet, prop_names, class_names}: DetailProps) {
case "ConsoleCmd":
return <>{packet.command}</>
case "UserCmd":
return <>{formatUserCmd(packet.cmd)}</>
return <UserCmdDetails cmd={packet.cmd}/>
case "DataTables":
return <>{packet.tables.length}</>
case "Stop":
@ -108,121 +110,3 @@ export function PacketDetails({packet, prop_names, class_names}: DetailProps) {
return <>{packet.tables.length}</>
}
}
function messageInfoText(msg: Message, prop_names: Map<number, { table: String, prop: String }>, class_names: Map<number, String>) {
switch (msg.type) {
case "Print":
return <>{msg.value}</>
case "ServerInfo":
return <>stv: {msg.stv ? 'true' : 'false'}, map: {msg.map}, player count: {msg.player_count},
map: {msg.map}</>
case "NetTick":
return <>Tick {msg.tick}, frame time: {msg.frame_time}, std_dev: {msg.std_dev}</>
case "ParseSounds":
return <>{msg.reliable ? 'reliable' : 'unreliable'} {msg.num} sounds: {msg.length} bits</>
case "VoiceInit":
return <>{msg.codec} at quality {msg.quality} and sampling rage {msg.sampling_rate}</>
case "SigOnState":
return <>state: {msg.state}, count: {msg.count}</>
case "SetConVar":
return <>{msg.vars.map(cvar => `${cvar.key}=${cvar.value}`).join(', ')}</>
case "SetView":
return <>set view to entity {msg.index}</>
case "GameEventList":
return <>{msg.event_list.map(formatEventDefinition).map((str, i) => (<p key={i}>{str}</p>))}</>
case "PacketEntities":
let entities = msg.entities.map(entity => formatEntity(entity, prop_names, class_names)).map((str, i) => <p
key={i}>{str}</p>);
let deleted = <></>
if (msg.removed_entities.length > 0) {
deleted = <>deleted: {msg.removed_entities.join(', ')}</>
}
return <>
<p>delta: {JSON.stringify(msg.delta)}</p>
<p>baseline: {JSON.stringify(msg.base_line)}</p>
<p>max: {JSON.stringify(msg.max_entries)}</p>
<p>updated base line: {JSON.stringify(msg.updated_base_line)}</p>
{entities}
{deleted}
</>
case "TempEntities":
let events = msg.events.map(event => {
let class_name = class_names.get(event.class_id);
let props = event.props.map(prop => {
let names = prop_names.get(prop.identifier);
if (names) {
return `${names.table}.${names.prop}=${formatPropValue(prop.value)}`;
} else {
return `[unknown prop]=${prop.value}`;
}
});
return `temp entity ${class_name}(delay: ${event.fire_delay}, reliable:${JSON.stringify(event.reliable)}): ` + props.join(', ');
})
return <>{events.map(event => <p>{event}</p>)}</>
default:
let json = msg;
delete json.type;
return <>{JSON.stringify(json)}</>
}
}
function formatUserCmd(cmd: UserCmd): string {
let out = `${cmd.command_number} - tick ${cmd.tick_count}: `;
const formatOptionNum = (x: number | null) => x === null ? 0 : x;
const anyNonNull = (xs: (number | null)[]) => xs.findIndex(x => x !== null) != -1;
let parts = [];
if (anyNonNull(cmd.view_angles)) {
parts.push(`view angles: ${cmd.view_angles.map(formatOptionNum).join(',')}`);
}
if (anyNonNull(cmd.movement)) {
parts.push(`movement: ${cmd.movement.map(formatOptionNum).join(',')}`);
}
if (cmd.mouse_dx !== null || cmd.mouse_dy !== null) {
parts.push(`mouse: ${[cmd.mouse_dx, cmd.mouse_dy].map(formatOptionNum).join(',')}`);
}
if (cmd.impulse) {
parts.push(`impulse ${cmd.impulse}`)
}
if (cmd.buttons) {
parts.push(`buttons ${cmd.buttons}`)
}
if (cmd.weapon_select) {
parts.push(`weapon ${cmd.weapon_select.select}(${cmd.weapon_select.subtype})`)
}
if (parts.length == 0) {
parts.push(`no data`)
}
return out + parts.join(', ');
}
function formatPropValue(value: SendPropValue): string {
if (Array.isArray(value)) {
return '[' + value.map(formatPropValue).join(',') + ']'
} else if (typeof value === "number" || typeof value === "string") {
return `${value}`
} else {
return JSON.stringify(value)
}
}
function formatEntity(entity: PacketEntity, prop_names: Map<number, { table: String, prop: String }>, class_names: Map<number, String>,): string {
let class_name = class_names.get(entity.server_class);
let props = entity.props.map(prop => {
let names = prop_names.get(prop.identifier);
if (names) {
return `${names.table}.${names.prop}=${formatPropValue(prop.value)}`;
} else {
return `[unknown prop]=${prop.value}`;
}
})
return `entity ${entity.entity_index}(${class_name}) ${entity.pvs}: ` + props.join(', ');
}
function formatEventDefinition(event: GameEventDefinition): string {
let values = event.entries.map(entry => `${entry.name}: ${entry.kind}`);
return `${event.event_type}{${values.join(', ')}}`;
}