Skip to content
Open

v0.4 #44

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
1a0d24c
More logs
dimspell Jul 14, 2025
6e03ac1
Fix degradation - TCP listener didnt start
dimspell Jul 14, 2025
bb17783
Less logs
dimspell Jul 14, 2025
b47cd61
Less logs
dimspell Jul 14, 2025
4fe0362
Print error
dimspell Jul 14, 2025
cae4d36
Less logs
dimspell Jul 14, 2025
5b74d19
This fragment causes a problem with receiving `##user` TCP packet
dimspell Jul 14, 2025
a96d874
Stop the connection after disconnect
dimspell Jul 14, 2025
b211c50
Use one function to simplify the disconnection
dimspell Jul 14, 2025
7cea0f4
Spin multiple fake host for testing
dimspell Jul 15, 2025
9055373
Better log / Remove unused code
dimspell Jul 17, 2025
49e5aae
Better naming / Simplify code
dimspell Jul 17, 2025
5d7aa3f
Simplify code
dimspell Jul 17, 2025
58f852d
Use interface to mock
dimspell Jul 17, 2025
2043577
Rename parameters
dimspell Jul 17, 2025
91c95c3
Remove unused func
dimspell Jul 17, 2025
ab0abdc
Test fake host closing
dimspell Jul 17, 2025
f21d5bd
Less logs
dimspell Jul 17, 2025
a8dd75d
Add some testers
dimspell Jul 17, 2025
b1eb295
Try to call disconnect from relay
dimspell Jul 17, 2025
e27b0d2
Try again with dial-tcp updated
dimspell Jul 17, 2025
4d9621b
Less logs
dimspell Jul 17, 2025
e9eb0b2
Clear all after 30s
dimspell Jul 17, 2025
1041fa4
Try to build on linux
dimspell Jul 18, 2025
1e0db06
Remove linux ci
dimspell Jul 18, 2025
a94fbd2
Disable probe
dimspell Jul 18, 2025
cb7430c
Better logs
dimspell Jul 18, 2025
3765e0a
Disable custom handshake
dimspell Jul 18, 2025
92b2a40
Revert
dimspell Jul 18, 2025
5eb7cff
Cleanup
dimspell Jul 18, 2025
cbb3413
Remove unused
dimspell Jul 18, 2025
911ae25
Stop just once
dimspell Jul 18, 2025
3ff1eb7
Simplify some code
dimspell Jul 20, 2025
7ddc681
vibe coding
dimspell Jul 21, 2025
f012bc5
Put all config parameters into Console struct
dimspell Jul 24, 2025
e31cbd2
Fix lan acceptance test
dimspell Jul 24, 2025
74461e1
Remove unused code
dimspell Jul 24, 2025
8f78617
The Big Proxy Rewrite
dimspell Jul 24, 2025
8c6443b
Fix panic occurring in Multiplayer
dimspell Jul 25, 2025
798204f
Rename Multiplayer as RoomService
dimspell Jul 25, 2025
ad4fd3f
Refactor Session Manager and move packet creation to packet module
dimspell Jul 25, 2025
4c079b0
Make a use of refactored SessionManager
dimspell Jul 25, 2025
3243459
Refactor / rearrange code
dimspell Jul 25, 2025
53250ee
Remove commented code
dimspell Jul 25, 2025
1f3181d
Remove packetlogger
dimspell Jul 27, 2025
ffda3c7
Use global logger, do not ignore the packets
dimspell Jul 27, 2025
2e13ce1
Fix relay listing rooms
dimspell Jul 27, 2025
0eab0c3
Use table test so the file is shorter and without duplicated code
dimspell Jul 27, 2025
423ae08
Log error with failure
dimspell Jul 27, 2025
c7694b4
The first packet - the handshake packet - was not sent
dimspell Jul 27, 2025
be33581
Send the packets (ignoring who is the author)
dimspell Jul 27, 2025
50b0628
Make use of proxy factory
dimspell Jul 25, 2025
47bc83b
Rename binary name
dimspell Jul 27, 2025
7ba239d
Remove unused markdown file
dimspell Jul 27, 2025
c8f27e6
Add generated documentation about the project
dimspell Jan 13, 2026
fdf7983
Add to git all changes, even those not working (fix later)
dimspell Jan 13, 2026
535e0a9
Add cursor rules
dimspell Jan 13, 2026
501b4f5
Fixes from the Cursor
dimspell Jan 13, 2026
4c8f173
Cursor attempt to rewrite the P2P
dimspell Jan 13, 2026
0e80638
Cross check for the P2P
dimspell Jan 13, 2026
90e8d21
Fix the problems in the P2P test
dimspell Jan 13, 2026
d0dd093
Add more P2P e2e tests
dimspell Jan 13, 2026
38a3a42
Add more tests
dimspell Jan 13, 2026
24e5efa
Add golang-ci as a tool
dimspell Jan 14, 2026
63099b4
Fix linter warnings
dimspell Jan 14, 2026
3bd9063
Fix skipped tests
dimspell Jan 14, 2026
02e2c60
Implement acceptance test for the relay proxy
dimspell Jan 14, 2026
8c09a34
Add libp2p proxy
dimspell Mar 2, 2026
6f7f619
Update dependencies
dimspell Jul 16, 2026
9969835
Update the deploy instructions, remove dead code
dimspell Jul 16, 2026
3f9f262
Address dead-locks and data races in proxies
dimspell Jul 16, 2026
b1367eb
Fix pre-existing test failures: mock ProxyFactory + Reset() race
dimspell Jul 16, 2026
06a8589
Fix pre-existing listener test races and goroutine-after-test panics
dimspell Jul 16, 2026
4840a93
fix(redirect): Drop UDP packets from unknown source (spoofing)
dimspell Jul 16, 2026
e641e1d
fix(redirect): Remove dead branch in ListenerUDP.Close
dimspell Jul 16, 2026
d7500d3
fix(redirect): Return TCP listener for OtherUserHasJoined in NewTCPRe…
dimspell Jul 16, 2026
198979e
fix(redirect): Derive AssignIP from IPPrefix bytes
dimspell Jul 16, 2026
e3e1096
fix(redirect): Copy bytes before OnReceive to avoid slice aliasing
dimspell Jul 16, 2026
8199340
fix(redirect): Exact-match RemoveByIP instead of prefix
dimspell Jul 16, 2026
c551f66
fix(redirect): Read full UDP handshake instead of fixed 4 bytes
dimspell Jul 16, 2026
9bfae94
fix(redirect): Correct NewTCPRedirect log to say TCP not UDP
dimspell Jul 16, 2026
97ff786
feat(integration): testcontainers spike + --run-mode console flag
dimspell Jul 16, 2026
c18972e
test(integration): Add LAN game-exchange test + harden backend handshake
dimspell Jul 16, 2026
64e97fd
Upgrade go version to 1.26
dimspell Jul 16, 2026
7a899ce
Implement missing HandleRelayJoin and HandleRelayDelete hooks
dimspell Jul 16, 2026
57c3731
Remove the webrtc stub
dimspell Jul 16, 2026
df3e6d5
Add disconnect status and cleanup on disconnect
dimspell Jul 17, 2026
24a342a
test(integration): Add test for the relay proxy
dimspell Jul 17, 2026
c26ac46
feat(console): Finite state machine for the user session
dimspell Jul 17, 2026
f078f60
test(integration): Add test for the webrtc proxy
dimspell Jul 17, 2026
a1f33af
feat(backend): Add ping and TCP keep-alive to auto-close stale conn
dimspell Jul 17, 2026
4ac09c6
pending(integration): Add test for 4 players in same room
dimspell Jul 17, 2026
995a9c2
test(console): Fix some deadlocks in the acceptance tests
dimspell Jul 17, 2026
39dda57
test(integration): Get rid of libp2p; Reimplement bufio stream
dimspell Jul 18, 2026
6f1da4f
test(relay): Implement a port for the QUIC Relay Transport
dimspell Jul 18, 2026
f133b02
test(p2p): Use transport interface
dimspell Jul 19, 2026
8e77f70
test(console): Fix race in tests
dimspell Jul 20, 2026
9a9941a
ci(tests): Add timeout to the tests
dimspell Jul 20, 2026
da31279
test(integration): Add test for the host migration
dimspell Jul 20, 2026
5484e49
test(redirect): Do not spam with logs when connection is down
dimspell Jul 20, 2026
9e8f376
Do not spam when connection is down
dimspell Jul 20, 2026
a2578f6
Try to get rid of the data race
dimspell Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Cursor rules for this Go project

These rules define how code should be written, reviewed, and changed in this repository.

## Go toolchain + formatting (non-negotiable)

- **Always run `gofmt`** on changed Go files.
- **Use `goimports`** (or `gofmt` + manual imports) so imports are grouped and unused imports removed.
- **Prefer standard library** over new dependencies unless there’s a clear reason.
- **Keep `go.mod` tidy**: don’t add deps casually; when you do, keep versions minimal and coherent.

## Code style + structure

- **Small, focused diffs**: change the minimum surface area necessary.
- **Package boundaries**: prefer adding code to an existing relevant package over creating new packages.
- **Avoid cyclic dependencies**; keep `internal/...` layering clean.
- **Public API discipline**:
- Export only what must be used outside the package.
- Keep exported types/functions well-documented (GoDoc comment starting with the symbol name).
- **Naming**:
- Use Go conventions (short receiver names, no `GetX()` unless needed).
- Prefer clear domain names over generic ones (e.g., `RoomService` not `Service`).

## Error handling (industry patterns)

- **Return errors, don’t panic** in normal control flow.
- **Wrap errors with context** using `%w`:
- `return fmt.Errorf("doing X: %w", err)`
- **Use sentinel errors sparingly**; prefer typed errors only when callers must branch on type.
- **Don’t log and return the same error** unless you have a strong reason (avoid double logging).
- **Use `errors.Is` / `errors.As`** for comparisons.

## Context, cancellation, timeouts

- **Plumb `context.Context`** through call chains for IO, network, and long-running operations.
- **Never store `context.Context`** inside structs.
- **Always set timeouts** for network calls unless the caller explicitly controls it.
- **Select on `ctx.Done()`** in loops that can block or run indefinitely.

## Concurrency hygiene

- **Avoid goroutine leaks**:
- Always ensure goroutines can exit (ctx cancellation, closed channels, done signals).
- Prefer `errgroup.Group` for fan-out work with cancellation.
- **Prefer channels for ownership transfer**, not for shared mutable state.
- **Protect shared state** with `sync.Mutex`/`sync.RWMutex` or confine it to one goroutine.
- **Avoid data races**: design for correctness first; use `-race` when running tests.

## Logging & observability

- Use the project’s logging conventions (currently `slog`).
- Log **actionable** fields:
- request/connection identifiers where available
- addresses, user IDs, session IDs (avoid PII where possible)
- **Don’t log secrets** (passwords, tokens, private keys).
- Metrics:
- Prefer incrementing existing Prometheus metrics where appropriate instead of inventing new ones.

## Networking & protocol code

- Be strict on input validation at boundaries (HTTP params, websocket payloads, TCP packets).
- For protocol evolution:
- Add backwards-compatible fields where possible.
- Keep parsing resilient; fail fast with clear errors on malformed inputs.
- Avoid allocating in tight loops; reuse buffers when it materially improves hot paths.

## Tests (expected behavior)

- For new behavior, add or update tests close to the code:
- `*_test.go` in the same package unless black-box testing is required.
- Prefer table-driven tests for multiple cases.
- Verify error cases explicitly (invalid inputs, timeouts, empty states).
- Keep tests deterministic; avoid sleeps when possible.

## Security expectations

- Treat all external input as untrusted (HTTP, WS, TCP, DB).
- Never commit real credentials; use env vars/config where secrets are needed.
- Prefer constant-time comparisons for secrets if relevant.
- Validate/normalize user-controlled network addresses to avoid SSRF-like patterns.

## Documentation when changing behavior

- If you modify user-facing behavior (CLI flags, endpoints, defaults), update docs in `docs/`.
- Keep “incomplete/WIP” notes accurate—don’t over-promise.

## How to work in this repository (practical)

- Before large refactors, look for existing patterns in `internal/...` and follow them.
- When you touch the backend protocol handlers (`internal/backend/command_*.go`), prioritize:
- correctness
- compatibility with existing clients
- clear logging around handshake/session boundaries

2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
root = true

[ansible/**]
[scripts/ansible/**]
charset = utf-8
end_of_line = lf
indent_size = 2
Expand Down
32 changes: 26 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,34 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.26"
cache: 'true'

- name: Compile binary
run: go build -v ./

- name: Run Tests
run: go test -v -cover -race ./...
run: go test -v -timeout 600s -cover -race ./...

- name: Run E2E Tests
run: go test -v -tags=e2e -timeout 1200s ./internal/acceptance/...

integration:
runs-on: ubuntu-latest
timeout-minutes: 20
needs:
- test
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.26"

- name: Run multi-docker integration tests
# Docker is pre-installed on GitHub-hosted runners.
run: go test -tags=integration -run 'TestSpike|TestLANGameExchange' -v -timeout 300s -count=1 ./internal/integration/...

build:
runs-on: ubuntu-latest
Expand All @@ -44,7 +64,7 @@ jobs:
id: setup-go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.26"

- name: Get build data
run: |
Expand Down Expand Up @@ -74,7 +94,7 @@ jobs:
-ldflags="main.version=${BUILD_VERSION}" \
-ldflags="main.commit=${BUILD_REVISION}" \
-ldflags="main.date=${BUILD_TIME}" \
-env="GOTOOLCHAIN=go1.24.4" \
-env="GOTOOLCHAIN=go1.26" \
-tags=gui

- name: Extract packaged app
Expand All @@ -89,7 +109,7 @@ jobs:
with:
name: windows-build
path: |
dist/Gladiator.exe
dist/Gladiator.exe

- name: Release
uses: softprops/action-gh-release@v2
Expand All @@ -115,7 +135,7 @@ jobs:
id: setup-go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.26"

- name: Get build data
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
id: setup-go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.26"

- name: Get build data
run: |
Expand Down Expand Up @@ -52,4 +52,4 @@ jobs:
--build-arg GIT_COMMIT=$BUILD_REVISION \
-t $IMAGE_NAME .
env:
IMAGE_NAME: "ghcr.io/${{ github.repository_owner }}/gladiator:latest"
IMAGE_NAME: "ghcr.io/${{ github.repository_owner }}/gladiator:latest"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ go.work

# Releases
*.zip

# deepwork state (git-local)
.slim/deepwork/
.dwp/
72 changes: 72 additions & 0 deletions DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Deploy

## Prerequisites

- **Ansible** installed locally (`brew install ansible` or `pip install ansible`)
- **SSH key** with root (or sudo-capable user) access to the target server
- A **domain name** pointing to the server's public IP

## Quick start

```bash
HOST=1.2.3.4 \
DOMAIN=gladiator.example.com \
EMAIL=admin@example.com \
./scripts/deploy.sh
```

That's it. The script will:

1. Install the required Ansible collection (`community.general`)
2. Run the playbook against the server

You can also go through the Makefile:

```bash
make deploy HOST=1.2.3.4 DOMAIN=gladiator.example.com EMAIL=admin@example.com
```

### Optional variables

| Variable | Default | Description |
|---|---|---|
| `SSH_USER` | `root` | SSH user on the target |
| `SSH_KEY` | *(none)* | Path to your SSH private key |
| `ANSIBLE_EXTRA_VARS` | *(none)* | Additional vars forwarded to Ansible (`k=v k=v`) |

## What gets installed

| Role | What it does |
|---|---|
| **podman** | Installs Podman, enables auto-update timer |
| **security** | Hardens SSH (key-only), enables UFW (ports 22/80/443/9999), installs fail2ban & unattended-upgrades |
| **appuser** | Creates unprivileged `appuser`, enables systemd lingering, sets file limits |
| **gladiator** | Deploys the container as a Quadlet systemd service via Podman |
| **nginx** | Installs nginx + certbot, provisions a Let's Encrypt certificate, configures reverse proxy |

## Building the container image locally

```bash
make docker-build
# or
docker compose build
```

This compiles the Go binary inside a multi-stage Docker build and produces the `gladiator` image. Run it locally:

```bash
docker compose up -d
```

## SSH reference

```bash
make ssh HOST=1.2.3.4 SSH_KEY=~/.ssh/id_ed25519
```

## Per-server notes

- The container runs rootless under `appuser` via Quadlet (Podman's systemd integration)
- Container auto-updates: `podman-auto-update.timer` checks for new images on a system timer
- The SQLite database persists in `/var/lib/gladiator/` on the host
- UDP port 9999 is the game relay; TCP 2137 is the HTTP console (proxied through nginx on 443)
21 changes: 13 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# syntax=docker/dockerfile:1
# Build stage
FROM golang:1-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /gladiator .

# Runtime stage
FROM gcr.io/distroless/static-debian12

# Add build-time metadata
ARG BUILD_DATE
ARG VERSION
ARG GIT_COMMIT
Expand All @@ -9,14 +18,10 @@ LABEL org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.version="${VERSION}" \
org.opencontainers.image.revision="${GIT_COMMIT}"

# Copy the compiled binary
COPY ./gladiator /gladiator
COPY --from=builder /gladiator /gladiator

# Name the directory for the volume
VOLUME /data

# Document the ports that will be exposed
EXPOSE 2137
EXPOSE 9999
EXPOSE 2137 9999

ENTRYPOINT ["/gladiator"]
ENTRYPOINT ["/gladiator"]
21 changes: 21 additions & 0 deletions Dockerfile.integration
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1
# Integration test image: gladiator server + mock game client.
# Used by testcontainers (FromDockerfile) for the multi-docker integration suite.
FROM golang:1-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /gladiator ./
RUN CGO_ENABLED=0 go build -o /mockclient ./cmd/integration-client

# Runtime: distroless static (no shell needed; one process per container).
FROM gcr.io/distroless/static-debian12

COPY --from=builder /gladiator /gladiator
COPY --from=builder /mockclient /mockclient

VOLUME /data
EXPOSE 2137 9999 6112

ENTRYPOINT ["/gladiator"]
23 changes: 22 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,34 @@ join_id ?= 2
build:
go build -race -v -o /dev/null ./

docker-build:
docker compose build

deploy:
./scripts/deploy.sh

serve:
go run -v ./ serve --backend-addr=127.0.0.1:6112 --console-addr=127.0.0.1:2137
#go run ./ serve --backend-addr=0.0.0.0:6112 --console-addr=0.0.0.0:2137
#(go build -v); (.\gladiator.exe serve --backend-addr=0.0.0.0:6112 --console-addr=0.0.0.0:2137)

test:
go test -v --race ./...
go test -v --race -timeout 600s ./...

test-e2e:
go test -v -tags=e2e -timeout 1200s ./internal/acceptance/...

test-integration-lan:
go test -tags=integration -run 'TestSpike|TestLANGameExchange' -v -timeout 300s -count=1 ./internal/integration/...

test-integration-relay:
go test -tags integration -run TestRelayGameExchange -v -timeout 300s -count=1 ./internal/integration/...

test-integration-lifecycle:
go test -tags integration -run 'TestRelayFullLifecycleWithMigration' -v -timeout 600s -count=1 ./internal/integration/...

lint:
go tool golangci-lint run ./...

console: clear
go run -v ./ console --console-addr=127.0.0.1:2137
Expand Down
20 changes: 0 additions & 20 deletions ansible/Makefile

This file was deleted.

5 changes: 0 additions & 5 deletions ansible/roles/grafana/defaults/main.yml

This file was deleted.

Loading
Loading