Skip to content

Commit 7f6b9eb

Browse files
authored
56 support demos in zst archive faceit (#68)
1 parent ab61f2c commit 7f6b9eb

File tree

9 files changed

+135
-66
lines changed

9 files changed

+135
-66
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ assets/icons/shapes
77
.secret/
88
*.wasm
99
web/public/wasm/
10+
testdemos/

cmd/wasm/wasm.go

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,63 +2,37 @@ package main
22

33
import (
44
"bytes"
5-
"compress/gzip"
6-
"csgo-2d-demo-player/pkg/message"
75
"csgo-2d-demo-player/pkg/parser"
86
"fmt"
9-
"io"
10-
"log"
117
"syscall/js"
12-
"time"
13-
14-
"github.com/markus-wa/demoinfocs-golang/v5/pkg/demoinfocs"
15-
"google.golang.org/protobuf/proto"
168
)
179

1810
func main() {
1911
done := make(chan struct{}, 0)
2012
fmt.Println("HEHEHEH")
21-
js.Global().Set("testt", js.FuncOf(testt))
13+
js.Global().Set("wasmParseDemo", js.FuncOf(wasmParseDemo))
2214
<-done
2315
}
2416

25-
func testt(this js.Value, args []js.Value) interface{} {
26-
fmt.Printf("testt: +%v\n", args[0].Get("length"))
27-
input := make([]byte, args[0].Get("length").Int())
28-
js.CopyBytesToGo(input, args[0])
29-
30-
fmt.Println("fer")
31-
32-
parse(bytes.NewReader(input), args[1])
33-
34-
return js.ValueOf("2")
35-
}
17+
func wasmParseDemo(this js.Value, args []js.Value) interface{} {
18+
fmt.Println("Herere")
19+
filename := args[0].String()
20+
fmt.Println("trying to parse a demo %s", &filename)
3621

37-
func parse(input io.Reader, callback js.Value) {
38-
fmt.Printf("callback? %+v\n", callback)
22+
callback := args[2]
23+
demoData := make([]byte, args[1].Get("length").Int())
24+
js.CopyBytesToGo(demoData, args[1])
3925

40-
gzipReader, streamErr := gzip.NewReader(input)
41-
if streamErr != nil {
42-
log.Printf("Failed to create gzip reader from demo. %+v", streamErr)
43-
}
44-
45-
startTime := time.Now().Local()
46-
err := parser.Parse(gzipReader, func(msg *message.Message, state demoinfocs.GameState) {
47-
fmt.Printf("message: %+v \n", msg.MsgType)
48-
49-
payload, protoErr := proto.Marshal(msg)
50-
if protoErr != nil {
51-
fmt.Printf("failed to marshall the message: %+v %+v\n", msg, protoErr)
52-
}
26+
err := parser.WasmParseDemo(filename, bytes.NewReader(demoData), func(payload []byte) {
5327
arrayConstructor := js.Global().Get("Uint8Array")
5428
dataJS := arrayConstructor.New(len(payload))
5529
js.CopyBytesToJS(dataJS, payload)
5630
callback.Invoke(dataJS)
5731
})
58-
fmt.Printf("parsing took: %s\n", time.Since(startTime))
5932

6033
if err != nil {
6134
fmt.Printf("Failed to parse a demo: %+v \n", err)
6235
}
63-
fmt.Println("demo parsed?")
36+
37+
return js.ValueOf("2")
6438
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/alexflint/go-arg v1.5.1
1010
github.com/golang/geo v0.0.0-20250813021530-247f39904721
1111
github.com/gorilla/websocket v1.5.3
12+
github.com/klauspost/compress v1.18.0
1213
github.com/markus-wa/demoinfocs-golang/v5 v5.0.2
1314
github.com/sparkoo/go-steam v0.0.0-20231112203532-968479d66868
1415
github.com/stretchr/testify v1.10.0
@@ -44,6 +45,7 @@ require (
4445
github.com/oklog/ulid/v2 v2.1.1 // indirect
4546
github.com/pkg/errors v0.9.1 // indirect
4647
github.com/pmezard/go-difflib v1.0.0 // indirect
48+
github.com/stretchr/objx v0.5.2 // indirect
4749
go.opencensus.io v0.24.0 // indirect
4850
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
4951
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBY
7979
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
8080
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
8181
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
82+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
83+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
8284
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
8385
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
8486
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=

pkg/parser/wasmparser.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package parser
2+
3+
import (
4+
"compress/gzip"
5+
"csgo-2d-demo-player/pkg/message"
6+
"fmt"
7+
"io"
8+
"strings"
9+
10+
"github.com/klauspost/compress/zstd"
11+
"github.com/markus-wa/demoinfocs-golang/v5/pkg/demoinfocs"
12+
"google.golang.org/protobuf/proto"
13+
)
14+
15+
func WasmParseDemo(demoFilename string, demoFile io.Reader, callback func(payload []byte)) error {
16+
decompressedDemo, decompressErr := decompress(demoFilename, demoFile)
17+
if decompressErr != nil {
18+
return decompressErr
19+
}
20+
21+
return Parse(decompressedDemo, func(msg *message.Message, state demoinfocs.GameState) {
22+
fmt.Printf("message: %+v \n", msg.MsgType)
23+
24+
payload, protoErr := proto.Marshal(msg)
25+
if protoErr != nil {
26+
fmt.Printf("failed to marshall the message: %+v %+v\n", msg, protoErr)
27+
}
28+
callback(payload)
29+
})
30+
}
31+
32+
func decompress(filename string, demoFile io.Reader) (io.Reader, error) {
33+
if strings.HasSuffix(filename, ".gz") {
34+
return gzip.NewReader(demoFile)
35+
}
36+
37+
if strings.HasSuffix(filename, ".zst") {
38+
return zstd.NewReader(demoFile)
39+
}
40+
41+
return nil, fmt.Errorf("unsupported file format %s", filename)
42+
}

pkg/parser/wasmparser_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package parser
2+
3+
import (
4+
"os"
5+
"testing"
6+
)
7+
8+
var zstDemofileName = "1-cde451fd-6cd4-4c87-a432-d97d2235021a-1-1.dem.zst"
9+
var gzDemofileName = "1-cde451fd-6cd4-4c87-a432-d97d2235021a-1-1.dem.gz"
10+
var testDemosFolderPath = "../../testdemos"
11+
12+
func TestParseZstDemoArchive(t *testing.T) {
13+
demoFile, err := os.Open(testDemosFolderPath + "/" + zstDemofileName)
14+
if err != nil {
15+
t.Skip("failed to open the demo testfile. skipping for now as I have testdemos just locally")
16+
}
17+
parseErr := WasmParseDemo(zstDemofileName, demoFile, func(payload []byte) {})
18+
if parseErr != nil {
19+
t.Fatalf("failed to parse the demo: %v", parseErr)
20+
}
21+
}
22+
23+
func TestParseGzDemoArchive(t *testing.T) {
24+
demoFile, err := os.Open(testDemosFolderPath + "/" + gzDemofileName)
25+
if err != nil {
26+
t.Skip("failed to open the demo testfile. skipping for now as I have testdemos just locally")
27+
}
28+
parseErr := WasmParseDemo(gzDemofileName, demoFile, func(payload []byte) {})
29+
if parseErr != nil {
30+
t.Fatalf("failed to parse the demo: %v", parseErr)
31+
}
32+
}
33+
34+
func TestParseUnsupportedDemoArchive(t *testing.T) {
35+
parseErr := WasmParseDemo("not_supported.demo", nil, func(payload []byte) {})
36+
if parseErr == nil {
37+
t.Fatalf("parse should fail: %v", parseErr)
38+
}
39+
}

web/public/worker.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,39 @@
1-
const serverHost = globalThis.location.host.includes("localhost") ? "http://localhost:8080" : "";
1+
const serverHost = globalThis.location.host.includes("localhost")
2+
? "http://localhost:8080"
3+
: "";
24

3-
importScripts('wasm/wasm_exec.js');
5+
importScripts("wasm/wasm_exec.js");
46

57
onmessage = (event) => {
6-
if (event.data instanceof Uint8Array) {
7-
globalThis.testt(event.data, async function (data) {
8+
console.log("received event: ", event);
9+
var demoData = event.data.data;
10+
var filename = event.data.filename;
11+
console.log("file: ", filename);
12+
if (demoData instanceof Uint8Array) {
13+
globalThis.wasmParseDemo(filename, demoData, async function (data) {
814
if (data instanceof Uint8Array) {
9-
postMessage(data)
15+
postMessage(data);
1016
// const msg = proto.Message.deserializeBinary(data).toObject()
1117
// messageBus.emit(msg)
1218
} else {
13-
console.log("[message] text data received from server, this is weird. We're using protobufs ?!?!?", data);
14-
postMessage(JSON.parse(data))
19+
console.log(
20+
"[message] text data received from server, this is weird. We're using protobufs ?!?!?",
21+
data
22+
);
23+
postMessage(JSON.parse(data));
1524
}
16-
})
25+
});
1726
}
18-
}
27+
};
1928

2029
async function loadWasm() {
21-
console.log("hus", serverHost + "/wasm")
2230
const go = new globalThis.Go();
23-
await WebAssembly.instantiateStreaming(fetch("/wasm/csdemoparser.wasm"), go.importObject)
24-
.then((result) => {
25-
go.run(result.instance);
26-
console.log("should be loaded now")
27-
});
31+
await WebAssembly.instantiateStreaming(
32+
fetch("/wasm/csdemoparser.wasm"),
33+
go.importObject
34+
).then((result) => {
35+
go.run(result.instance);
36+
console.log("should be loaded now");
37+
});
2838
}
2939
loadWasm();

web/src/Index/Uploader/Uploader.jsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1-
import './Uploader.css';
2-
import { FileUpload } from 'primereact/fileupload';
3-
import { useLocation } from 'preact-iso';
4-
import { useContext } from 'react';
5-
import { DemoContext } from '../../context'
1+
import "./Uploader.css";
2+
import { FileUpload } from "primereact/fileupload";
3+
import { useLocation } from "preact-iso";
4+
import { useContext } from "react";
5+
import { DemoContext } from "../../context";
66

77
const Uploader = () => {
88
const demoData = useContext(DemoContext);
99
const { route } = useLocation();
1010

1111
const uploadHandler = function ({ files }) {
1212
const [file] = files;
13-
console.log(file)
1413

1514
const reader = new FileReader();
1615

1716
reader.onload = function (e) {
1817
const arrayBuffer = e.target.result;
1918
const byteArray = new Uint8Array(arrayBuffer);
20-
demoData.setDemoData(byteArray)
21-
route("/player")
19+
demoData.setDemoData({ filename: file.name, data: byteArray });
20+
route("/player");
2221
// const uuid = crypto.randomUUID()
2322
// window.open("/player?platform=upload&uuid=" + uuid, '_blank').focus();
2423
// const channel = new BroadcastChannel(uuid);
@@ -27,7 +26,7 @@ const Uploader = () => {
2726
// }, 1000)
2827
};
2928
reader.readAsArrayBuffer(file);
30-
}
29+
};
3130

3231
return (
3332
<div>
@@ -39,9 +38,10 @@ const Uploader = () => {
3938
// onProgress={onProgress}
4039
customUpload={true}
4140
uploadHandler={uploadHandler}
42-
auto />
41+
auto
42+
/>
4343
</div>
44-
)
45-
}
44+
);
45+
};
4646

4747
export default Uploader;

web/src/Player/PlayerApp.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import MessageBus from "./MessageBus.js";
55
import Player from "./Player.js";
66
import Map2d from "./map/Map2d.jsx";
77
import InfoPanel from "./panel/InfoPanel.jsx";
8-
// import '../libs/wasm_exec.js';
98
import './protos/Message_pb.js'
109
import DemoContext from "../context.js"
11-
// import workerScript from "./worker.js";
1210

1311
export function PlayerApp() {
1412
const demoData = useContext(DemoContext);
@@ -26,6 +24,7 @@ export function PlayerApp() {
2624
};
2725

2826
useEffect(() => {
27+
console.log("isWasmLoaded", isWasmLoaded)
2928
if (demoData.demoData) {
3029
setTimeout(() => worker.postMessage(demoData.demoData), 1000)
3130
}

0 commit comments

Comments
 (0)