@velo/battlefield (0.1.0)
Installation
@velo:registry=npm install @velo/battlefield@0.1.0"@velo/battlefield": "0.1.0"About this package
@velo/battlefield
Game-neutral input and buffered authoritative movement, with no runtime dependencies. This ESM
package publishes JavaScript, declarations and source maps from dist/. Node 22 or later is
supported; input helpers run in a browser with DOM and pointer-lock support.
Install and imports
Configure the public Forgejo registry in the consuming project's .npmrc:
@velo:registry=https://git.amhk.dk/api/packages/velo/npm/
Install @velo/battlefield with npm and commit the consumer's lockfile. Import from the single
public entry point; source files and deep imports are not part of the package contract:
import {
UnitMotion,
LockedCursor,
bindMinimapInput,
edgeDirection,
CAMERA_NAVIGATION,
clamp,
type Point,
type MovingEntity,
type UnitPose,
} from '@velo/battlefield';
Units and lifecycle
Pointis{ x, z }in battlefield metres.MovingEntityadds numericidandfacing;UnitPoseaddsfacing, in radians.UnitMotion(bufferSeconds = 0.1)receives authoritative entities withreceive(entities, serverSeconds, receivedMs, playing). Server time and the buffer use seconds; receive/frame clocks use monotonic milliseconds from the same clock. CallprepareFrame(nowMs)once per animation frame, thenpose(id)for interpolation. It takes the shortest facing arc, rejects older playing snapshots, removes missing entities from retained history and never extrapolates past the latest snapshot. Use a buffer at least as long as sparse sample intervals.reset()clears history when switching battles;playing = falsealso resets it. It owns no listeners or animation loop.bindMinimapInput(canvas, actions, signal)converts canvas pixels into map metres usingactions.mapSize(). Left drag moves the camera; right press issues an order or cancels building placement. The caller owns theAbortController: abort on teardown to remove canvas/window listeners, release drag capture and clear drag styling. Supply a live signal when binding.LockedCursor(canvas, hooks)creates a.locked-game-cursormarker and installs document/window listeners. Hooks receive cursor screen pixels, battlefield hover state, lock changes and errors. Calltoggle()from user activation;release()exits lock. Calldispose()on teardown to release lock, remove listeners and remove the marker, including late pointer-lock acquisition. The consumer supplies marker styling and owns its canvas.edgeDirection(positionPixels, extentPixels)returns signed edge-scroll strength.CAMERA_NAVIGATIONdocuments its configuration through named pixel, viewport-per-second and second fields.clamp(value, minimum, maximum)bounds a number.
Package development
Run from this directory, independently of either game:
npm ci
npm run typecheck
npx tsx --test test/capabilities.test.mjs
npm run check:consumer
npm run build
npm test is the package-local source-test command. check:consumer compiles with the local
TypeScript compiler, packs the artifact, installs it and TypeScript in a temporary project outside
the repository, then type-checks and imports all public exports. It checks tarball contents and
removes the fixture afterwards. No source aliases or sibling files are used by that consumer.
The build writes only ignored dist/ output. npm pack includes dist/, this README and npm's
required manifest; build before manually packing.
For optional unpublished integration testing, build here, then run
npm install --no-save ../../packages/battlefield from a game directory (or use npm link). Keep
registry dependencies and lockfiles committed; local links are temporary. Any game source alias
must be explicitly opt-in and must stay disabled in CI and container builds.
Publication
.forgejo/workflows/publish-battlefield.yml checks, builds and publishes when main changes
packages/battlefield/**, or on manual dispatch. It uses PACKAGE_REGISTRY_TOKEN to publish to
https://git.amhk.dk/api/packages/velo/npm/. There are no inter-package dependencies to publish
first. The workflow chooses the next available 0.x.0 release above the registry's matching
versions; the committed version is the initial floor, not a fixed release version. Consumers
update their version range and lockfile deliberately: ^0.N.0 accepts only 0.N.x.
Dependencies
Development dependencies
| ID | Version |
|---|---|
| jsdom | ^26.1.0 |
| tsx | ^4.23.15 |
| typescript | ^5.9.3 |