mirror of
https://codeberg.org/demostf/tf-demos-viewer.git
synced 2026-06-03 18:14:11 +02:00
transfer data to js wip
This commit is contained in:
parent
3e7b885236
commit
59c410566e
9 changed files with 338 additions and 59 deletions
|
|
@ -39,10 +39,10 @@ npm test -- --safari
|
||||||
|
|
||||||
* `webpack.config.js` contains the Webpack configuration. You shouldn't need to change this, unless you have very special needs.
|
* `webpack.config.js` contains the Webpack configuration. You shouldn't need to change this, unless you have very special needs.
|
||||||
|
|
||||||
* The `js` folder contains your JavaScript code (`index.js` is used to hook everything into Webpack, you don't need to change it).
|
* The `js` folder contains your JavaScript code (`index.ts` is used to hook everything into Webpack, you don't need to change it).
|
||||||
|
|
||||||
* The `src` folder contains your Rust code.
|
* The `src` folder contains your Rust code.
|
||||||
|
|
||||||
* The `static` folder contains any files that you want copied as-is into the final build. It contains an `index.html` file which loads the `index.js` file.
|
* The `static` folder contains any files that you want copied as-is into the final build. It contains an `index.html` file which loads the `index.ts` file.
|
||||||
|
|
||||||
* The `tests` folder contains your Rust unit tests.
|
* The `tests` folder contains your Rust unit tests.
|
||||||
|
|
|
||||||
24
js/index.js
24
js/index.js
|
|
@ -1,24 +0,0 @@
|
||||||
import("../pkg/index.js")
|
|
||||||
.then(m => {
|
|
||||||
document.getElementById('file').onchange = e => {
|
|
||||||
let file = e.target.files[0];
|
|
||||||
|
|
||||||
let reader = new FileReader();
|
|
||||||
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
|
|
||||||
reader.onload = function() {
|
|
||||||
console.log(reader.result);
|
|
||||||
let bytes = new Uint8Array(reader.result);
|
|
||||||
|
|
||||||
console.time('demo_parse');
|
|
||||||
m.parse_demo(bytes);
|
|
||||||
console.timeEnd('demo_parse');
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.onerror = function() {
|
|
||||||
console.log(reader.error);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
138
js/index.ts
Normal file
138
js/index.ts
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
import {FlatState, XY} from '../pkg/index.d.ts';
|
||||||
|
|
||||||
|
import("../pkg/index.js")
|
||||||
|
.then(m => {
|
||||||
|
document.getElementById('file').onchange = e => {
|
||||||
|
let file = (e.target as HTMLInputElement).files[0];
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
|
||||||
|
reader.onload = function () {
|
||||||
|
let bytes = new Uint8Array(reader.result as ArrayBuffer);
|
||||||
|
|
||||||
|
console.time('demo_parse');
|
||||||
|
const state = m.parse_demo(bytes);
|
||||||
|
|
||||||
|
console.timeEnd('demo_parse');
|
||||||
|
console.time('transfer');
|
||||||
|
|
||||||
|
let playerCount = m.get_player_count(state);
|
||||||
|
let boundaries = m.get_boundaries(state);
|
||||||
|
let data = m.get_data(state);
|
||||||
|
|
||||||
|
let parsed = new ParsedDemo(playerCount, {
|
||||||
|
boundary_min: {
|
||||||
|
x: boundaries.boundary_min.x,
|
||||||
|
y: boundaries.boundary_min.y,
|
||||||
|
},
|
||||||
|
boundary_max: {
|
||||||
|
x: boundaries.boundary_max.x,
|
||||||
|
y: boundaries.boundary_max.y,
|
||||||
|
}
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
console.timeEnd('transfer');
|
||||||
|
|
||||||
|
console.log(parsed, parsed.getPlayer(100, 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = function () {
|
||||||
|
console.log(reader.error);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
|
||||||
|
enum Team {
|
||||||
|
Other = 0,
|
||||||
|
Spectator = 1,
|
||||||
|
Red = 2,
|
||||||
|
Blue = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Class {
|
||||||
|
Other = 0,
|
||||||
|
Scout = 1,
|
||||||
|
Sniper = 2,
|
||||||
|
Solder = 3,
|
||||||
|
Demoman = 4,
|
||||||
|
Medic = 5,
|
||||||
|
Heavy = 6,
|
||||||
|
Pyro = 7,
|
||||||
|
Spy = 8,
|
||||||
|
Engineer = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WorldBoundaries {
|
||||||
|
boundary_min: {
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
},
|
||||||
|
boundary_max: {
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlayerState {
|
||||||
|
position: {
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
},
|
||||||
|
angle: number,
|
||||||
|
health: number,
|
||||||
|
team: Team,
|
||||||
|
playerClass: Class,
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpack_f32(val: number, min: number, max: number): number {
|
||||||
|
const ratio = val / (Math.pow(2, 16) - 1);
|
||||||
|
return ratio * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpack_angle(val: number): number {
|
||||||
|
const ratio = val / (Math.pow(2, 8) - 1);
|
||||||
|
return ratio * 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParsedDemo {
|
||||||
|
private playerCount: number;
|
||||||
|
private world: WorldBoundaries;
|
||||||
|
private data: Uint8Array;
|
||||||
|
private tickCount: number;
|
||||||
|
|
||||||
|
constructor(playerCount: number, world: WorldBoundaries, data: Uint8Array) {
|
||||||
|
this.playerCount = playerCount;
|
||||||
|
this.world = world;
|
||||||
|
this.data = data;
|
||||||
|
this.tickCount = data.length / playerCount / PACK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlayer(tick: number, playerIndex: number): PlayerState {
|
||||||
|
if (playerIndex >= this.playerCount) {
|
||||||
|
throw new Error("Player out of bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = ((playerIndex * this.tickCount) + tick) * PACK_SIZE;
|
||||||
|
return unpackPlayer(this.data, base, this.world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PACK_SIZE = 8;
|
||||||
|
|
||||||
|
function unpackPlayer(bytes: Uint8Array, base: number, world: WorldBoundaries): PlayerState {
|
||||||
|
const x = unpack_f32(bytes[base] + (bytes[base + 1] << 8), world.boundary_min.x, world.boundary_max.x);
|
||||||
|
const y = unpack_f32(bytes[base + 2] + (bytes[base + 3] << 8), world.boundary_min.y, world.boundary_max.y);
|
||||||
|
let health = bytes[base + 4] + (bytes[base + 5] << 8);
|
||||||
|
const angle = unpack_angle(bytes[base + 6]);
|
||||||
|
const team = (bytes[base + 7] >> 4) as Team;
|
||||||
|
const playerClass = (bytes[base + 7] & 15) as Class;
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: {x, y},
|
||||||
|
angle,
|
||||||
|
health,
|
||||||
|
team,
|
||||||
|
playerClass
|
||||||
|
}
|
||||||
|
}
|
||||||
78
package-lock.json
generated
78
package-lock.json
generated
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "rust-webpack-template",
|
"name": "tf-demos-viewer",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
|
@ -3855,6 +3855,12 @@
|
||||||
"sha.js": "^2.4.8"
|
"sha.js": "^2.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"picomatch": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||||
|
|
@ -5000,6 +5006,70 @@
|
||||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ts-loader": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-Dd9FekWuABGgjE1g0TlQJ+4dFUfYGbYcs52/HQObE0ZmUNjQlmLAS7xXsSzy23AMaMwipsx5sNHvoEpT2CZq1g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^2.3.0",
|
||||||
|
"enhanced-resolve": "^4.0.0",
|
||||||
|
"loader-utils": "^1.0.2",
|
||||||
|
"micromatch": "^4.0.0",
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"braces": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fill-range": "^7.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fill-range": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"micromatch": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"braces": "^3.0.1",
|
||||||
|
"picomatch": "^2.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||||
|
|
@ -5028,6 +5098,12 @@
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "3.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
|
||||||
|
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"union-value": {
|
"union-value": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,11 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@wasm-tool/wasm-pack-plugin": "^0.4.2",
|
"@wasm-tool/wasm-pack-plugin": "^0.4.2",
|
||||||
"copy-webpack-plugin": "^5.0.3",
|
"copy-webpack-plugin": "^5.0.3",
|
||||||
|
"rimraf": "^2.6.3",
|
||||||
|
"ts-loader": "^6.2.1",
|
||||||
|
"typescript": "^3.7.5",
|
||||||
"webpack": "^4.33.0",
|
"webpack": "^4.33.0",
|
||||||
"webpack-cli": "^3.3.3",
|
"webpack-cli": "^3.3.3",
|
||||||
"webpack-dev-server": "^3.7.1",
|
"webpack-dev-server": "^3.7.1"
|
||||||
"rimraf": "^2.6.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
src/lib.rs
81
src/lib.rs
|
|
@ -3,7 +3,8 @@
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
use crate::state::ParsedDemo;
|
use crate::state::ParsedDemo;
|
||||||
use tf_demo_parser::demo::parser::gamestateanalyser::GameStateAnalyser;
|
use tf_demo_parser::demo::parser::gamestateanalyser::{GameStateAnalyser, World};
|
||||||
|
use tf_demo_parser::demo::vector::Vector;
|
||||||
use tf_demo_parser::{Demo, DemoParser, ParseError};
|
use tf_demo_parser::{Demo, DemoParser, ParseError};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
|
@ -14,14 +15,77 @@ macro_rules! log {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn parse_demo(buffer: Box<[u8]>) -> Result<ParsedDemo, JsValue> {
|
#[derive(Debug, Clone, Copy)]
|
||||||
let buffer = buffer.into_vec();
|
pub struct XY {
|
||||||
let parsed = parse_demo_inner(buffer).map_err(|e| JsValue::from(e.to_string()))?;
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
Ok(parsed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_demo_inner(buffer: Vec<u8>) -> Result<ParsedDemo, ParseError> {
|
impl From<Vector> for XY {
|
||||||
|
fn from(vec: Vector) -> Self {
|
||||||
|
XY { x: vec.x, y: vec.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct WorldBoundaries {
|
||||||
|
pub boundary_min: XY,
|
||||||
|
pub boundary_max: XY,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<World> for WorldBoundaries {
|
||||||
|
fn from(world: World) -> Self {
|
||||||
|
WorldBoundaries {
|
||||||
|
boundary_min: world.boundary_min.into(),
|
||||||
|
boundary_max: world.boundary_max.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub struct FlatState {
|
||||||
|
player_count: usize,
|
||||||
|
data: Box<[u8]>,
|
||||||
|
boundaries: WorldBoundaries,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlatState {
|
||||||
|
pub fn new(parsed: ParsedDemo, world: World) -> Self {
|
||||||
|
FlatState {
|
||||||
|
player_count: parsed.players.len(),
|
||||||
|
boundaries: world.into(),
|
||||||
|
data: parsed.flat().into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn parse_demo(buffer: Box<[u8]>) -> Result<FlatState, JsValue> {
|
||||||
|
let buffer = buffer.into_vec();
|
||||||
|
let (parsed, world) = parse_demo_inner(buffer).map_err(|e| JsValue::from(e.to_string()))?;
|
||||||
|
|
||||||
|
let world = world.ok_or_else(|| JsValue::from_str("No world defined in demo"))?;
|
||||||
|
|
||||||
|
Ok(FlatState::new(parsed, world))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn get_boundaries(state: &FlatState) -> WorldBoundaries {
|
||||||
|
state.boundaries.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn get_player_count(state: &FlatState) -> usize {
|
||||||
|
state.player_count
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn get_data(state: FlatState) -> Box<[u8]> {
|
||||||
|
state.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_demo_inner(buffer: Vec<u8>) -> Result<(ParsedDemo, Option<World>), ParseError> {
|
||||||
let demo = Demo::new(buffer);
|
let demo = Demo::new(buffer);
|
||||||
let parser = DemoParser::new_with_analyser(demo.get_stream(), GameStateAnalyser::default());
|
let parser = DemoParser::new_with_analyser(demo.get_stream(), GameStateAnalyser::default());
|
||||||
let (_header, mut ticker) = parser.ticker()?;
|
let (_header, mut ticker) = parser.ticker()?;
|
||||||
|
|
@ -37,7 +101,8 @@ pub fn parse_demo_inner(buffer: Vec<u8>) -> Result<ParsedDemo, ParseError> {
|
||||||
skip = !skip;
|
skip = !skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(parsed_demo)
|
let world: Option<&World> = ticker.state().world.as_ref();
|
||||||
|
Ok((parsed_demo, world.map(|w| w.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is like the `main` function, except for JavaScript.
|
// This is like the `main` function, except for JavaScript.
|
||||||
|
|
|
||||||
13
src/state.rs
13
src/state.rs
|
|
@ -1,6 +1,5 @@
|
||||||
use tf_demo_parser::demo::parser::gamestateanalyser::{Class, GameState, Team, World};
|
use tf_demo_parser::demo::parser::gamestateanalyser::{Class, GameState, Team, World};
|
||||||
use tf_demo_parser::demo::vector::VectorXY;
|
use tf_demo_parser::demo::vector::VectorXY;
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
pub struct Angle(u8);
|
pub struct Angle(u8);
|
||||||
|
|
@ -19,11 +18,10 @@ impl From<Angle> for f32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ParsedDemo {
|
pub struct ParsedDemo {
|
||||||
tick: usize,
|
pub tick: usize,
|
||||||
players: Vec<Vec<u8>>,
|
pub players: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedDemo {
|
impl ParsedDemo {
|
||||||
|
|
@ -61,6 +59,13 @@ impl ParsedDemo {
|
||||||
.iter()
|
.iter()
|
||||||
.fold(0, |size, player| size + player.len())
|
.fold(0, |size, player| size + player.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flat(self) -> Vec<u8> {
|
||||||
|
self.players
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|player| player.into_iter())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
|
|
|
||||||
8
tsconfig.json
Normal file
8
tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es6",
|
||||||
|
"allowJs": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,25 +5,34 @@ const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||||
const dist = path.resolve(__dirname, "dist");
|
const dist = path.resolve(__dirname, "dist");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: "production",
|
mode: "production",
|
||||||
entry: {
|
entry: {
|
||||||
index: "./js/index.js"
|
index: "./js/index.ts"
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: dist,
|
path: dist,
|
||||||
filename: "[name].js"
|
filename: "[name].js"
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: dist,
|
contentBase: dist,
|
||||||
},
|
},
|
||||||
plugins: [
|
module: {
|
||||||
new CopyPlugin([
|
rules: [
|
||||||
path.resolve(__dirname, "static")
|
{
|
||||||
]),
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules|pkg/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CopyPlugin([
|
||||||
|
path.resolve(__dirname, "static")
|
||||||
|
]),
|
||||||
|
|
||||||
new WasmPackPlugin({
|
new WasmPackPlugin({
|
||||||
crateDirectory: __dirname,
|
crateDirectory: __dirname,
|
||||||
extraArgs: "--out-name index"
|
extraArgs: "--out-name index"
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue