mirror of
https://codeberg.org/demostf/inspector.git
synced 2026-06-03 18:14:08 +02:00
search
This commit is contained in:
parent
6dfc8bc4fb
commit
8de1835d29
10 changed files with 282 additions and 73 deletions
|
|
@ -37,3 +37,6 @@ wasm-bindgen-test = "0.3.13"
|
|||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = true
|
||||
14
src/lib.rs
14
src/lib.rs
|
|
@ -1,14 +1,15 @@
|
|||
mod search;
|
||||
mod utils;
|
||||
|
||||
use crate::utils::set_panic_hook;
|
||||
use crate::search::{packet_matches, SearchFilter};
|
||||
use bitbuffer::{BitRead, BitReadBuffer, BitReadStream, LittleEndian};
|
||||
use js_sys::Function;
|
||||
use serde::Serialize;
|
||||
use tf_demo_parser::demo::header::Header;
|
||||
use tf_demo_parser::demo::packet::datatable::{DataTablePacket, SendTableName, ServerClassName};
|
||||
use tf_demo_parser::demo::packet::Packet;
|
||||
use tf_demo_parser::demo::parser::DemoHandler;
|
||||
use tf_demo_parser::demo::parser::RawPacketStream;
|
||||
use tf_demo_parser::demo::parser::{DemoHandler, NullHandler};
|
||||
use tf_demo_parser::demo::sendprop::SendPropName;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
|
@ -144,6 +145,15 @@ impl Parser {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn search(&self, filter: JsValue) -> Vec<usize> {
|
||||
let filter: SearchFilter = filter.into_serde().unwrap();
|
||||
self.packets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, packet)| packet_matches(packet, &filter).then_some(index))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
|||
154
src/search.rs
Normal file
154
src/search.rs
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use tf_demo_parser::demo::message::gameevent::GameEventMessage;
|
||||
use tf_demo_parser::demo::message::packetentities::{PacketEntitiesMessage, PacketEntity};
|
||||
use tf_demo_parser::demo::message::setconvar::SetConVarMessage;
|
||||
use tf_demo_parser::demo::message::stringtable::{
|
||||
CreateStringTableMessage, UpdateStringTableMessage,
|
||||
};
|
||||
use tf_demo_parser::demo::message::tempentities::TempEntitiesMessage;
|
||||
use tf_demo_parser::demo::message::{
|
||||
EntityMessage, FileMessage, GetCvarValueMessage, Message, PrintMessage, SetViewMessage,
|
||||
StringCmdMessage,
|
||||
};
|
||||
use tf_demo_parser::demo::packet::consolecmd::ConsoleCmdPacket;
|
||||
use tf_demo_parser::demo::packet::message::MessagePacket;
|
||||
use tf_demo_parser::demo::packet::stringtable::{StringTableEntry, StringTablePacket};
|
||||
use tf_demo_parser::demo::packet::Packet;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SearchFilter {
|
||||
pub entity: u32,
|
||||
pub search: String,
|
||||
#[serde(default)]
|
||||
pub prop_ids: Vec<u64>,
|
||||
#[serde(default)]
|
||||
pub class_ids: Vec<u32>,
|
||||
}
|
||||
|
||||
impl SearchFilter {
|
||||
pub fn has_entity_filter(&self) -> bool {
|
||||
!self.search.is_empty() || !self.prop_ids.is_empty() || !self.class_ids.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn packet_matches(packet: &Packet, filter: &SearchFilter) -> bool {
|
||||
// return false;
|
||||
// if packet
|
||||
// .packet_type()
|
||||
// .as_lowercase_str()
|
||||
// .contains(&filter.search)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
match packet {
|
||||
Packet::Signon(MessagePacket { messages, .. })
|
||||
| Packet::Message(MessagePacket { messages, .. }) => messages
|
||||
.iter()
|
||||
.any(|message| message_matches(message, filter)),
|
||||
Packet::SyncTick(_) => false,
|
||||
Packet::ConsoleCmd(ConsoleCmdPacket { command, .. }) => command.contains(&filter.search),
|
||||
Packet::UserCmd(_) => false,
|
||||
Packet::DataTables(_) => false,
|
||||
Packet::Stop(_) => false,
|
||||
Packet::StringTables(StringTablePacket { tables, .. }) => tables.iter().any(|table| {
|
||||
table.name.contains(&filter.search)
|
||||
|| table.entries.iter().any(|(_, entry)| {
|
||||
entry
|
||||
.text
|
||||
.as_deref()
|
||||
.map(|text| text.contains(&filter.search))
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn message_matches(message: &Message, filter: &SearchFilter) -> bool {
|
||||
let has_search = !filter.search.is_empty();
|
||||
match message {
|
||||
Message::File(FileMessage { file_name, .. }) => {
|
||||
has_search && file_name.contains(&filter.search)
|
||||
}
|
||||
Message::StringCmd(StringCmdMessage { command, .. }) => {
|
||||
has_search && command.contains(&filter.search)
|
||||
}
|
||||
Message::SetConVar(SetConVarMessage { vars, .. }) => {
|
||||
has_search
|
||||
&& vars.iter().any(|var| {
|
||||
var.key.contains(&filter.search) || var.value.contains(&filter.search)
|
||||
})
|
||||
}
|
||||
Message::Print(PrintMessage { value }) => {
|
||||
has_search && value.as_ref().contains(&filter.search)
|
||||
}
|
||||
Message::CreateStringTable(CreateStringTableMessage { table, .. }) => {
|
||||
has_search && table.name.contains(&filter.search)
|
||||
|| table
|
||||
.entries
|
||||
.iter()
|
||||
.any(|(_, entry)| string_entry_matches(entry, filter))
|
||||
}
|
||||
Message::UpdateStringTable(UpdateStringTableMessage { entries, .. }) => {
|
||||
has_search
|
||||
&& entries
|
||||
.iter()
|
||||
.any(|(_, entry)| string_entry_matches(entry, filter))
|
||||
}
|
||||
Message::SetView(SetViewMessage { index }) => (*index as u32) == filter.entity,
|
||||
Message::UserMessage(_) => false,
|
||||
Message::EntityMessage(EntityMessage { class_id, .. }) => {
|
||||
filter.class_ids.contains(&(*class_id as u32))
|
||||
}
|
||||
Message::GameEvent(GameEventMessage { event, .. }) => {
|
||||
has_search && event.event_type().as_str().contains(&filter.search)
|
||||
}
|
||||
Message::PacketEntities(PacketEntitiesMessage {
|
||||
entities,
|
||||
removed_entities,
|
||||
..
|
||||
}) => {
|
||||
(removed_entities.contains(&filter.entity.into()) && !filter.has_entity_filter())
|
||||
|| entities.iter().any(|entity| entity_matches(entity, filter))
|
||||
}
|
||||
Message::TempEntities(TempEntitiesMessage { events }) => events.iter().any(|event| {
|
||||
filter
|
||||
.class_ids
|
||||
.contains(&(u16::from(event.class_id).into()))
|
||||
}),
|
||||
Message::GetCvarValue(GetCvarValueMessage { value, .. }) => {
|
||||
has_search && value.contains(&filter.search)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn string_entry_matches(entry: &StringTableEntry, filter: &SearchFilter) -> bool {
|
||||
(!filter.search.is_empty())
|
||||
&& entry
|
||||
.text
|
||||
.as_deref()
|
||||
.map(|text| text.contains(&filter.search))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn entity_matches(entity: &PacketEntity, filter: &SearchFilter) -> bool {
|
||||
if entity.entity_index != filter.entity && filter.entity != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !filter.has_entity_filter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if filter
|
||||
.class_ids
|
||||
.contains(&u16::from(entity.server_class).into())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
entity
|
||||
.props
|
||||
.iter()
|
||||
.any(|prop| filter.prop_ids.contains(&prop.identifier.into()))
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#[allow(dead_code)]
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ import {Packet} from "./parser";
|
|||
import React, {useCallback, Component} from 'react'
|
||||
import {useDropzone} from 'react-dropzone'
|
||||
import ReactDOM from "react-dom";
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import {Header} from "./header";
|
||||
import {PacketDetails, PacketTable} from "./table";
|
||||
import {filterPacket, Search, SearchBar} from "./search";
|
||||
import {filterPacket, isSearchEmpty, SearchBar, SearchFilter} from "./search";
|
||||
import {ResponseMessageData} from "./rpc";
|
||||
|
||||
let _style = require('../styles/style.css');
|
||||
|
||||
|
|
@ -34,15 +36,11 @@ interface AppState {
|
|||
class_names: Map<number, string>,
|
||||
active: Packet | null,
|
||||
activeIndex: number | null,
|
||||
search: Search,
|
||||
search: SearchFilter,
|
||||
matches: number[],
|
||||
worker: Worker
|
||||
}
|
||||
|
||||
type MessageData = { type: "progress", progress: number }
|
||||
| { type: "packet", packet: Packet }
|
||||
| { type: "done", packets: PacketMeta[], header: Header, prop_names: { identifier: number, table: string, prop: string }[], class_names: { identifier: number, name: string }[] }
|
||||
| { type: "packet_names", packet: {} };
|
||||
|
||||
class App extends Component<{}, AppState> {
|
||||
state: AppState = {
|
||||
loading: false,
|
||||
|
|
@ -55,20 +53,27 @@ class App extends Component<{}, AppState> {
|
|||
activeIndex: null,
|
||||
search: {
|
||||
entity: 0,
|
||||
filter: "",
|
||||
classIds: [],
|
||||
propIds: [],
|
||||
search: "",
|
||||
class_ids: [],
|
||||
prop_ids: [],
|
||||
},
|
||||
matches: [],
|
||||
worker: null
|
||||
}
|
||||
|
||||
onSearch = debounce((search: Search) => this.setState({search}), 500)
|
||||
onSearch = debounce((search: SearchFilter) => {
|
||||
if (!isSearchEmpty(search)) {
|
||||
console.log(search);
|
||||
this.state.worker.postMessage({type: "search", filter: search});
|
||||
}
|
||||
this.setState({search});
|
||||
}, 500)
|
||||
|
||||
load(data: ArrayBuffer) {
|
||||
this.setState({loading: true});
|
||||
const worker = new Worker('./worker.js');
|
||||
this.setState({worker});
|
||||
worker.addEventListener("message", (event: MessageEvent<MessageData>) => {
|
||||
worker.addEventListener("message", (event: MessageEvent<ResponseMessageData>) => {
|
||||
const data = event.data;
|
||||
if (data.type !== "progress") {
|
||||
console.log(data);
|
||||
|
|
@ -96,7 +101,11 @@ class App extends Component<{}, AppState> {
|
|||
});
|
||||
break;
|
||||
case "packet":
|
||||
this.setState({active: data.packet})
|
||||
this.setState({active: data.packet});
|
||||
break;
|
||||
case "search_result":
|
||||
this.setState({matches: data.matches});
|
||||
break;
|
||||
}
|
||||
});
|
||||
worker.postMessage({
|
||||
|
|
@ -106,12 +115,11 @@ class App extends Component<{}, AppState> {
|
|||
}
|
||||
|
||||
filteredPackets(): PacketMeta[] {
|
||||
return this.state.packets;
|
||||
// if (this.state.search.filter || this.state.search.entity) {
|
||||
// return this.state.packets.filter(packet => filterPacket(packet, this.state.search));
|
||||
// } else {
|
||||
// return this.state.packets;
|
||||
// }
|
||||
if (isSearchEmpty(this.state.search)) {
|
||||
return this.state.packets;
|
||||
} else {
|
||||
return this.state.matches.map(index => this.state.packets[index]);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -130,6 +138,7 @@ class App extends Component<{}, AppState> {
|
|||
)
|
||||
} else if (this.state.packets.length) {
|
||||
let active = <></>;
|
||||
const packets = this.filteredPackets();
|
||||
if (this.state.active) {
|
||||
active = <div className="details"><PacketDetails packet={this.state.active}
|
||||
search={this.state.search}
|
||||
|
|
@ -141,7 +150,7 @@ class App extends Component<{}, AppState> {
|
|||
<SearchBar onSearch={this.onSearch} class_names={this.state.class_names}
|
||||
prop_names={this.state.prop_names}/>
|
||||
<div className="packets">
|
||||
<PacketTable packets={this.filteredPackets()} class_names={this.state.class_names}
|
||||
<PacketTable packets={packets} class_names={this.state.class_names}
|
||||
activeIndex={this.state.activeIndex}
|
||||
prop_names={this.state.prop_names}
|
||||
onClick={(index) => {
|
||||
|
|
@ -161,12 +170,9 @@ class App extends Component<{}, AppState> {
|
|||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<App/>
|
||||
,
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container);
|
||||
root.render(<App/>);
|
||||
|
||||
function DemoDropzone({onDrop}: { onDrop: (data: ArrayBuffer) => void }) {
|
||||
const onDropCb = useCallback((acceptedFiles: File[]) => {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import {EventInfo, GameEventDefinition, Message, PacketEntity, SendPropValue} from "../parser";
|
||||
import React from "react";
|
||||
import {filterEntity, filterMessage, Search} from "../search";
|
||||
import {filterEntity, filterMessage, isSearchEmpty, SearchFilter} from "../search";
|
||||
|
||||
export interface MessageInfoProps {
|
||||
msg: Message,
|
||||
prop_names: Map<number, { table: String, prop: String }>,
|
||||
class_names: Map<number, String>,
|
||||
search: Search
|
||||
search: SearchFilter
|
||||
}
|
||||
|
||||
export function MessageInfo({msg, prop_names, class_names, search}: MessageInfoProps) {
|
||||
|
|
@ -105,22 +105,22 @@ function formatEventDefinition(event: GameEventDefinition): string {
|
|||
return `${event.event_type}{${values.join(', ')}}`;
|
||||
}
|
||||
|
||||
function filteredEntities(entities: PacketEntity[], search: Search) {
|
||||
if (search.filter || search.entity) {
|
||||
function filteredEntities(entities: PacketEntity[], search: SearchFilter) {
|
||||
if (!isSearchEmpty(search)) {
|
||||
return entities.filter(entities => {
|
||||
return (search.entity == 0 || search.entity == entities.entity_index) &&
|
||||
(search.filter.length < 3 || filterEntity(entities.server_class, entities.baseline_props.concat(entities.props), search))
|
||||
(search.search.length < 3 || filterEntity(entities.server_class, entities.props, search))
|
||||
});
|
||||
} else {
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
|
||||
function filteredTempEntities(entities: EventInfo[], search: Search) {
|
||||
function filteredTempEntities(entities: EventInfo[], search: SearchFilter) {
|
||||
if (search.entity) {
|
||||
return [];
|
||||
}
|
||||
if (search.filter) {
|
||||
if (!isSearchEmpty(search)) {
|
||||
return entities.filter(entities => filterEntity(entities.class_id, entities.props, search));
|
||||
} else {
|
||||
return entities;
|
||||
|
|
|
|||
14
www/src/rpc.ts
Normal file
14
www/src/rpc.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import {SearchFilter} from "./search";
|
||||
import {Packet} from "./parser";
|
||||
import {Header} from "./header";
|
||||
import {PacketMeta} from "./index";
|
||||
|
||||
export type RequestMessageData = {type: "data", data: ArrayBuffer}
|
||||
| {type: "get", packet: number}
|
||||
| {type: "search", filter: SearchFilter}
|
||||
|
||||
export type ResponseMessageData = { type: "progress", progress: number }
|
||||
| { type: "packet", packet: Packet }
|
||||
| { type: "done", packets: PacketMeta[], header: Header, prop_names: { identifier: number, table: string, prop: string }[], class_names: { identifier: number, name: string }[] }
|
||||
| { type: "packet_names", packet: {} }
|
||||
| { type: "search_result", matches: number[] };
|
||||
|
|
@ -3,15 +3,15 @@ import React, {ChangeEvent, Component} from "react";
|
|||
import './search.css'
|
||||
import {Message, Packet, SendProp, StringTable} from "./parser";
|
||||
|
||||
export interface Search {
|
||||
filter: string,
|
||||
export interface SearchFilter {
|
||||
entity: number,
|
||||
propIds: number[],
|
||||
classIds: number[],
|
||||
search: string,
|
||||
prop_ids: number[],
|
||||
class_ids: number[],
|
||||
}
|
||||
|
||||
export interface SearchBarProps {
|
||||
onSearch: (search: Search) => void,
|
||||
onSearch: (search: SearchFilter) => void,
|
||||
prop_names: Map<number, { table: string, prop: string }>,
|
||||
class_names: Map<number, string>,
|
||||
}
|
||||
|
|
@ -27,12 +27,12 @@ export class SearchBar extends Component<SearchBarProps, SearchBarState> {
|
|||
entity: 0
|
||||
}
|
||||
|
||||
getSearch(): Search {
|
||||
getSearch(): SearchFilter {
|
||||
return {
|
||||
filter: this.state.filter,
|
||||
search: this.state.filter,
|
||||
entity: this.state.entity,
|
||||
propIds: filterPropNames(this.props.prop_names, this.state.filter),
|
||||
classIds: filterClassNames(this.props.class_names, this.state.filter),
|
||||
prop_ids: filterPropNames(this.props.prop_names, this.state.filter),
|
||||
class_ids: filterClassNames(this.props.class_names, this.state.filter),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ export class SearchBar extends Component<SearchBarProps, SearchBarState> {
|
|||
|
||||
export function filterPacket(
|
||||
packet: Packet,
|
||||
search: Search,
|
||||
search: SearchFilter,
|
||||
): boolean {
|
||||
switch (packet.type) {
|
||||
case "Signon":
|
||||
|
|
@ -79,7 +79,7 @@ export function filterPacket(
|
|||
case "SyncTick":
|
||||
return false;
|
||||
case "ConsoleCmd":
|
||||
return search.entity == 0 && packet.command.includes(search.filter);
|
||||
return search.entity == 0 && packet.command.includes(search.search);
|
||||
case "UserCmd":
|
||||
return false;
|
||||
case "DataTables":
|
||||
|
|
@ -92,6 +92,9 @@ export function filterPacket(
|
|||
}
|
||||
|
||||
function filterPropNames(prop_names: Map<number, { table: string, prop: string }>, filter: string): number[] {
|
||||
if (filter.length === 0) {
|
||||
return [];
|
||||
}
|
||||
filter = filter.toLowerCase();
|
||||
let ids = [];
|
||||
for (let [id, {table, prop}] of prop_names.entries()) {
|
||||
|
|
@ -103,6 +106,9 @@ function filterPropNames(prop_names: Map<number, { table: string, prop: string }
|
|||
}
|
||||
|
||||
function filterClassNames(class_names: Map<number, string>, filter: string): number[] {
|
||||
if (filter.length === 0) {
|
||||
return [];
|
||||
}
|
||||
filter = filter.toLowerCase();
|
||||
let ids = [];
|
||||
for (let [id, name] of class_names.entries()) {
|
||||
|
|
@ -115,54 +121,64 @@ function filterClassNames(class_names: Map<number, string>, filter: string): num
|
|||
|
||||
export function filterMessage(
|
||||
message: Message,
|
||||
search: Search,
|
||||
search: SearchFilter,
|
||||
): boolean {
|
||||
switch (message.type) {
|
||||
case "File":
|
||||
return search.entity == 0 && message.file_name.includes(search.filter);
|
||||
return search.entity == 0 && message.file_name.includes(search.search);
|
||||
case "StringCmd":
|
||||
return search.entity == 0 && message.command.includes(search.filter);
|
||||
return search.entity == 0 && message.command.includes(search.search);
|
||||
case "SetConVar":
|
||||
return search.entity == 0 && message.vars.some(cvar => cvar.value.includes(search.filter) || cvar.key.includes(search.filter));
|
||||
return search.entity == 0 && message.vars.some(cvar => cvar.value.includes(search.search) || cvar.key.includes(search.search));
|
||||
case "Print":
|
||||
return search.entity == 0 && message.value.includes(search.filter);
|
||||
return search.entity == 0 && message.value.includes(search.search);
|
||||
case "ClassInfo":
|
||||
return search.entity == 0 && message.entries.some(entry => entry.class_name.includes(search.filter) || entry.table_name.includes(search.filter));
|
||||
return search.entity == 0 && message.entries.some(entry => entry.class_name.includes(search.search) || entry.table_name.includes(search.search));
|
||||
case "CreateStringTable":
|
||||
return search.entity == 0 && filterStringTable(message.table, search);
|
||||
case "UpdateStringTable":
|
||||
return search.entity == 0 && message.entries.some(([_index, entry]) => (entry.text && entry.text.includes(search.filter)));
|
||||
return search.entity == 0 && message.entries.some(([_index, entry]) => (entry.text && entry.text.includes(search.search)));
|
||||
case "SetView":
|
||||
return search.entity == 0 && message.index === search.entity;
|
||||
case "SayText2":
|
||||
return search.entity == 0 && ((message.text && message.text.includes(search.filter)) || (message.from && message.from.includes(search.filter)));
|
||||
return search.entity == 0 && ((message.text && message.text.includes(search.search)) || (message.from && message.from.includes(search.search)));
|
||||
case "Text":
|
||||
return search.entity == 0 && message.text.includes(search.filter);
|
||||
return search.entity == 0 && message.text.includes(search.search);
|
||||
case "EntityMessage":
|
||||
return search.entity == 0 && search.classIds.includes(message.class_id)
|
||||
return search.entity == 0 && search.class_ids.includes(message.class_id)
|
||||
case "GameEvent":
|
||||
return search.entity == 0 && message.event.type.includes(search.filter)
|
||||
return search.entity == 0 && message.event.type.includes(search.search)
|
||||
case "PacketEntities":
|
||||
return message.removed_entities.includes(search.entity) || message.entities.some(entity => (search.entity == 0 || entity.entity_index == search.entity) && filterEntity(entity.server_class, entity.baseline_props.concat(entity.props), search))
|
||||
return message.removed_entities.includes(search.entity)
|
||||
|| message.entities.some(entity => (search.entity == 0 || entity.entity_index == search.entity)
|
||||
&& filterEntity(entity.server_class, entity.props, search))
|
||||
case "TempEntities":
|
||||
return search.entity == 0 && message.events.some(event => filterEntity(event.class_id, event.props, search))
|
||||
case "GetCvarValue":
|
||||
return search.entity == 0 && message.value.includes(search.filter);
|
||||
return search.entity == 0 && message.value.includes(search.search);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function filterEntity(class_id: number, props: SendProp[], search: Search): boolean {
|
||||
return search.classIds.includes(class_id) || props.some(prop => search.propIds.includes(prop.identifier))
|
||||
|| props.some(prop => prop.value == search.filter);
|
||||
export function filterEntity(class_id: number, props: SendProp[], search: SearchFilter): boolean {
|
||||
if (search.search.length === 0 && search.class_ids.length === 0 && search.prop_ids.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return search.class_ids.includes(class_id) || props.some(prop => search.prop_ids.includes(prop.identifier))
|
||||
|| props.some(prop => prop.value == search.search);
|
||||
}
|
||||
|
||||
function filterStringTable(table: StringTable, search: Search): boolean {
|
||||
if (table.name.includes(search.filter)) {
|
||||
function filterStringTable(table: StringTable, search: SearchFilter): boolean {
|
||||
if (table.name.includes(search.search)) {
|
||||
return true;
|
||||
} else if (table.entries.some(([_index, entry]) => entry.text.includes(search.filter))) {
|
||||
} else if (table.entries.some(([_index, entry]) => entry.text.includes(search.search))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isSearchEmpty(filter: SearchFilter) {
|
||||
return filter.search.length === 0 && filter.entity === 0 && filter.class_ids.length === 0 && filter.prop_ids.length === 0
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import {GameEventDefinition, Message, Packet, PacketEntity, SendPropValue, UserC
|
|||
import {FixedSizeList as List} from 'react-window';
|
||||
import {MessageInfo} from "./packets/message";
|
||||
import {UserCmdDetails} from "./packets/usercmd";
|
||||
import {filterMessage, filterPacket, Search} from "./search";
|
||||
import {filterMessage, filterPacket, SearchFilter} from "./search";
|
||||
import {PacketMeta, PacketType} from "./index"
|
||||
|
||||
interface TableProps {
|
||||
|
|
@ -81,11 +81,11 @@ interface DetailProps {
|
|||
packet: Packet,
|
||||
prop_names: Map<number, { table: string, prop: string }>,
|
||||
class_names: Map<number, string>,
|
||||
search: Search,
|
||||
search: SearchFilter,
|
||||
}
|
||||
|
||||
function filteredMessages(messages: Message[], search: Search) {
|
||||
if (search.filter || search.entity) {
|
||||
function filteredMessages(messages: Message[], search: SearchFilter) {
|
||||
if (search.search || search.entity) {
|
||||
return messages.filter(message => filterMessage(message, search));
|
||||
} else {
|
||||
return messages;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import {Parser} from "demo-inspector";
|
||||
import {RequestMessageData, ResponseMessageData} from "./rpc";
|
||||
|
||||
declare function postMessage(message: any): void;
|
||||
declare function postMessage(message: ResponseMessageData): void;
|
||||
|
||||
let parser: Parser | null = null;
|
||||
|
||||
type MessageData = {type: "data", data: ArrayBuffer} | {type: "get", packet: number}
|
||||
|
||||
onmessage = function (event: MessageEvent<MessageData>) {
|
||||
onmessage = function (event: MessageEvent<RequestMessageData>) {
|
||||
const data = event.data;
|
||||
switch (data.type) {
|
||||
case "data":
|
||||
|
|
@ -24,6 +23,12 @@ onmessage = function (event: MessageEvent<MessageData>) {
|
|||
})
|
||||
})
|
||||
break;
|
||||
case "search":
|
||||
if (parser) {
|
||||
const matches = parser.search(data.filter);
|
||||
postMessage({type: "search_result", matches: Array.prototype.slice.call(matches)})
|
||||
}
|
||||
break;
|
||||
case "get":
|
||||
if (parser) {
|
||||
const packet = parser.packet(data.packet);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue