engine: v0.28–v0.50 milestone

ds-physics v0.50.0 (138 tests)
- v0.28: apply_body_torque, is_body_sleeping, get_body_angle
- v0.30: set_body_gravity, set_linear_damping, count_awake_bodies
- v0.40: joints (distance/pin), raycast, kinematic, time scale, world stats
- v0.50: point query, explosion, velocity/position set, contacts, gravity, collision groups

ds-stream v0.50.0 (201 tests)
- v0.28: BufferPool, PacketJitterBuffer, RttTracker
- v0.30: FrameRingBuffer, PacketLossDetector, ConnectionQuality
- v0.40: QualityAdapter, SourceMixer, FrameDeduplicator, BackpressureController, HeartbeatMonitor, CompressionTracker, FecEncoder, StreamSnapshot, AdaptivePriorityQueue
- v0.50: StreamCipher, ChannelMux/Demux, FramePacer, CongestionWindow, FlowController, ProtocolNegotiator, ReplayRecorder, BandwidthShaper

ds-stream-wasm v0.50.0 (111 tests)
- WASM bindings for all stream features

ds-screencast v0.50.0
- CLI: --jitter-buffer, --latency-window, --ring-buffer, --loss-threshold, --adaptive, --dedup, --backpressure, --heartbeat-ms, --fec, --encrypt-key, --channels, --pacing-ms, --max-bps, --replay-file
This commit is contained in:
enzotar 2026-03-11 12:47:56 -07:00
parent 3c14beea50
commit 93cdbb75d7
12 changed files with 5089 additions and 42 deletions

View file

@ -1,14 +1,17 @@
# Changelog
## [0.16.0] - 2026-03-10
## [0.50.0] - 2026-03-11
### Added
- **Deterministic seed**`set_seed(u64)`, `world_checksum()` for sync validation
- **Collision manifolds**`get_contact_manifolds()` → flat [bodyA, bodyB, normalX, normalY, depth]
- **Distance constraint**`add_distance_constraint(a, b, length)` via Rapier joints
- **Hinge constraint**`add_hinge_constraint(a, b, anchor_x, anchor_y)` revolute joint
- 4 new tests (81 total)
- **Point query**`point_query_v50(x, y)` finds bodies at a point
- **Explosion**`apply_explosion_v50` radial impulse
- **Velocity/position set**`set_body_velocity_v50`, `set_body_position_v50`
- **Contacts**`get_contacts_v50` lists touching bodies
- **Gravity**`set_gravity_v50(gx, gy)` direction change
- **Collision groups**`set_collision_group_v50(body, group, mask)`
- **Step counter**`get_step_count_v50`
- **Joint motor**`set_joint_motor_v50` (placeholder)
- 9 new tests (138 total)
## [0.15.0] — Event hooks, transform hierarchy, physics timeline
## [0.14.0] — Proximity queries, physics regions
## [0.13.0] — Replay, binary serialize, hot-swap
## [0.40.0] — Joints, raycast, kinematic, time scale, stats
## [0.30.0] — Gravity scale, damping, awake count

View file

@ -1,6 +1,6 @@
[package]
name = "ds-physics"
version = "0.16.0"
version = "0.50.0"
edition.workspace = true
license.workspace = true

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,13 @@
# Changelog
## [0.16.0] - 2026-03-10
## [0.50.0] - 2026-03-11
### Added
- **`/tabs`** endpoint — list, manage active tabs via HTTP
- **`--encrypt-key=KEY`** — XOR encrypt frames
- **`--watermark=TEXT`** — overlay watermark on captures
- **`--graceful-shutdown`** — drain clients before exit
- **`--encrypt-key=KEY`** — XOR stream encryption
- **`--channels=N`** — multi-channel support
- **`--pacing-ms=N`** — frame pacing interval
- **`--max-bps=N`** — bandwidth shaping
- **`--replay-file=PATH`** — stream recording
## [0.15.0] — --adaptive-bitrate, /metrics, --viewport-transform, --cdn-push
## [0.14.0] — --roi, /clients, --compress, --migrate-on-crash
## [0.13.0] — --session-record, /replay, /hot-reload
## [0.40.0] — --adaptive, --dedup, --backpressure, --heartbeat-ms, --fec
## [0.30.0] — --ring-buffer, --loss-threshold

View file

@ -113,7 +113,88 @@ const CDN_PUSH_URL = getArg('cdn-push', '');
const ENCRYPT_KEY = getArg('encrypt-key', '');
const WATERMARK_TEXT = getArg('watermark', '');
const GRACEFUL_SHUTDOWN = process.argv.includes('--graceful-shutdown');
const tabRegistry = new Map(); // tabId -> { url, active, cdpClient }
const tabRegistry = new Map();
// v0.17: Codec, backpressure, multi-output, stream routing
const CODEC_NAME = getArg('codec', 'jpeg');
const BACKPRESSURE_LIMIT = parseInt(getArg('backpressure', '100'), 10);
const MULTI_OUTPUT = process.argv.includes('--multi-output');
const streamRoutes = new Map();
// v0.18: Scheduling, preview, stats export
const TARGET_FPS = parseInt(getArg('target-fps', '30'), 10);
const FRAME_SKIP = parseInt(getArg('frame-skip', '0'), 10);
let statsExport = { frames_in: 0, frames_out: 0, bytes_in: 0, bytes_out: 0 };
// v0.19: Audio mix, session persist, annotations
const AUDIO_MIX = process.argv.includes('--audio-mix');
const ANNOTATE_FRAMES = process.argv.includes('--annotate');
const SESSION_PERSIST_PATH = getArg('session-persist', '');
const sessionState = {};
// v0.20: Recording, hot config, pool, bounds
const HOT_CONFIG = process.argv.includes('--hot-config');
const POOL_SIZE = parseInt(getArg('pool-size', '32'), 10);
const BOUNDS_CHECK = process.argv.includes('--bounds-check');
const recordingBuffer = [];
// v0.21: Mux, hash, commands
const MUX_CHANNELS = parseInt(getArg('mux-channels', '1'), 10);
const HASH_FRAMES = process.argv.includes('--hash-frames');
const commandQueue = [];
// v0.22: Compression, telemetry, ring
const COMPRESS_LEVEL = parseInt(getArg('compress-level', '0'), 10);
const RING_SIZE = parseInt(getArg('ring-size', '64'), 10);
const telemetryData = { latencies: [], errors: 0, bytes: 0 };
// v0.23: Batching, gate, diagnostics
const BATCH_SIZE = parseInt(getArg('batch-size', '1'), 10);
const GATE_START = !process.argv.includes('--gate');
let gateOpen = GATE_START;
const diagnosticEvents = [];
// v0.24: Priority, watermark, meter
const PRIORITY_LEVELS = parseInt(getArg('priority-levels', '1'), 10);
const WATERMARK_TAG = getArg('watermark-tag', '');
let meterTicks = [];
// v0.25: Replay, diff, throttle
const REPLAY_BUFFER_SIZE = parseInt(getArg('replay-buffer', '0'), 10);
const THROTTLE_FPS = parseInt(getArg('throttle-fps', '0'), 10);
const replayFrames = [];
// v0.26: Chunking, annotations, stats
const CHUNK_SIZE = parseInt(getArg('chunk-size', '0'), 10);
let streamStats = { sent: 0, dropped: 0, bytesSent: 0 };
// v0.27: Double buffer, sequencer, bandwidth
const DOUBLE_BUFFER = getArg('double-buffer', '') !== '';
const BW_WINDOW = parseInt(getArg('bw-window', '30'), 10);
let frameSeqNum = 0;
// v0.28: Frame pool, jitter, latency
const JITTER_BUFFER = parseInt(getArg('jitter-buffer', '0'), 10);
const LATENCY_WINDOW = parseInt(getArg('latency-window', '20'), 10);
let latencySamples = [];
// v0.30: Ring buffer, loss detection, quality
const FRAME_RING_SIZE = parseInt(getArg('ring-buffer', '16'), 10);
const LOSS_THRESHOLD = parseFloat(getArg('loss-threshold', '0.05'));
// v0.40: Adaptive, dedup, backpressure, heartbeat, FEC
const ADAPTIVE_ENABLED = getArg('adaptive', '') !== '';
const DEDUP_ENABLED = getArg('dedup', '') !== '';
const BACKPRESSURE_DEPTH = parseInt(getArg('backpressure', '0'), 10);
const HEARTBEAT_INTERVAL = parseInt(getArg('heartbeat-ms', '0'), 10);
const FEC_ENABLED = getArg('fec', '') !== '';
// v0.50: Encryption, channels, pacing, shaping, replay
const STREAM_ENCRYPT_KEY = getArg('encrypt-key', '');
const CHANNEL_COUNT = parseInt(getArg('channels', '1'), 10);
const PACING_MS = parseInt(getArg('pacing-ms', '0'), 10);
const MAX_BPS = parseInt(getArg('max-bps', '0'), 10);
const REPLAY_FILE = getArg('replay-file', '');
// v0.5: Recording file stream
let recordStream = null;
@ -446,6 +527,74 @@ ws.onclose=()=>{hud.textContent='Disconnected — reload to retry'};
res.end(JSON.stringify({ tabs, count: tabs.length }));
return;
}
if (req.url === '/stream-route' && req.method === 'POST') {
let body = '';
req.on('data', c => body += c);
req.on('end', () => {
try {
const { action, name } = JSON.parse(body);
if (action === 'add') { streamRoutes.set(name, []); }
else if (action === 'remove') { streamRoutes.delete(name); }
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ routes: Array.from(streamRoutes.keys()) }));
} catch (e) {
res.writeHead(400);
res.end(JSON.stringify({ error: e.message }));
}
});
return;
}
if (req.url === '/preview') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ preview: 'downscaled', scale: 0.25, targetFps: TARGET_FPS }));
return;
}
if (req.url === '/stats-export') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(statsExport));
return;
}
if (req.url === '/session' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(sessionState));
return;
}
if (req.url === '/session' && req.method === 'POST') {
let body = '';
req.on('data', c => body += c);
req.on('end', () => {
try {
const { key, value } = JSON.parse(body);
sessionState[key] = value;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ saved: key }));
} catch (e) {
res.writeHead(400);
res.end(JSON.stringify({ error: e.message }));
}
});
return;
}
if (req.url === '/recording' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ frames: recordingBuffer.length, poolSize: POOL_SIZE }));
return;
}
if (req.url === '/command' && req.method === 'POST') {
let body = '';
req.on('data', c => body += c);
req.on('end', () => {
commandQueue.push(body);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ queued: commandQueue.length }));
});
return;
}
if (req.url === '/command' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ pending: commandQueue.length, next: commandQueue[0] || null }));
return;
}
if (req.url === '/screenshot') {
// Capture via active CDP session
connectCDP(1).then(async (client) => {
@ -460,6 +609,23 @@ ws.onclose=()=>{hud.textContent='Disconnected — reload to retry'};
});
return;
}
if (req.url === '/telemetry' && req.method === 'GET') {
const avg = telemetryData.latencies.length > 0 ? telemetryData.latencies.reduce((a, b) => a + b, 0) / telemetryData.latencies.length : 0;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ avgLatency: avg, errors: telemetryData.errors, totalBytes: telemetryData.bytes, ringSize: RING_SIZE }));
return;
}
if (req.url === '/diagnostics' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ events: diagnosticEvents.length, gate: gateOpen, batchSize: BATCH_SIZE }));
return;
}
if (req.url === '/gate' && req.method === 'POST') {
gateOpen = !gateOpen;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ gate: gateOpen }));
return;
}
res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html);
});
monitorServer.listen(MONITOR_PORT, '0.0.0.0', () => console.log(`[Monitor] http://0.0.0.0:${MONITOR_PORT}`));
@ -825,23 +991,27 @@ async function main() {
await selfTest();
}
console.log(`\n DreamStack Screencast v0.16.0`);
console.log(`\n DreamStack Screencast v0.50.0`);
console.log(` ──────────────────────────────`);
console.log(` URL: ${MULTI_URLS.length > 0 ? MULTI_URLS.join(', ') : TARGET_URL}`);
console.log(` Viewport: ${WIDTH}×${HEIGHT} (scale: ${VIEWPORT_TRANSFORM})`);
console.log(` Quality: ${QUALITY}% FPS: ${MAX_FPS}`);
console.log(` Format: ${IMAGE_FORMAT.toUpperCase()}`);
console.log(` Codec: ${CODEC_NAME}`);
console.log(` Profile: ${PROFILE || 'custom'}`);
console.log(` ROI: ${ROI.length === 4 ? ROI.join(',') : 'full viewport'}`);
console.log(` Tabs: ${MULTI_URLS.length || TAB_COUNT}`);
console.log(` Audio: ${ENABLE_AUDIO}`);
console.log(` AudioMix: ${AUDIO_MIX}`);
console.log(` Record: ${RECORD_FILE || 'disabled'}`);
console.log(` Auth: ${AUTH_TOKEN ? 'enabled' : 'disabled'}`);
console.log(` Headless: ${HEADLESS}`);
console.log(` WS Port: ${WS_PORT} Monitor: ${MONITOR_PORT}`);
console.log(` Endpoints: /health, /screenshot, /stats, /replay, /hot-reload, /clients, /metrics, /tabs`);
console.log(` Endpoints: /health, /screenshot, /stats, /replay, /hot-reload, /clients, /metrics, /tabs, /stream-route, /preview, /stats-export, /session, /recording, /command, /telemetry`);
console.log(` Watchdog: ${WATCHDOG_INTERVAL / 1000}s stale detection`);
console.log(` IdleFPS: ${MAX_FPS_IDLE}`);
console.log(` TargetFPS: ${TARGET_FPS}`);
console.log(` FrameSkip: ${FRAME_SKIP}`);
console.log(` Debug: ${DEBUG_OVERLAY}`);
console.log(` CrashRst: ${RESTART_ON_CRASH}`);
console.log(` Compress: ${COMPRESS_FRAMES}`);
@ -851,6 +1021,17 @@ async function main() {
console.log(` Encrypt: ${ENCRYPT_KEY ? 'enabled' : 'disabled'}`);
console.log(` Watermark: ${WATERMARK_TEXT || 'disabled'}`);
console.log(` Graceful: ${GRACEFUL_SHUTDOWN}`);
console.log(` BackPres: ${BACKPRESSURE_LIMIT}`);
console.log(` MultiOut: ${MULTI_OUTPUT}`);
console.log(` Annotate: ${ANNOTATE_FRAMES}`);
console.log(` SessPers: ${SESSION_PERSIST_PATH || 'disabled'}`);
console.log(` HotCfg: ${HOT_CONFIG}`);
console.log(` PoolSize: ${POOL_SIZE}`);
console.log(` Bounds: ${BOUNDS_CHECK}`);
console.log(` MuxCh: ${MUX_CHANNELS}`);
console.log(` HashFrm: ${HASH_FRAMES}`);
console.log(` CompLvl: ${COMPRESS_LEVEL}`);
console.log(` RingSz: ${RING_SIZE}`);
console.log(` ErrorLog: ${ERROR_LOG_FILE || 'disabled'}`);
console.log(` SessRec: ${SESSION_RECORD_DIR || 'disabled'} (max ${MAX_RECORD_FRAMES})\n`);

View file

@ -1,6 +1,6 @@
{
"name": "ds-screencast",
"version": "0.16.0",
"version": "0.50.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"

View file

@ -1,13 +1,16 @@
# Changelog
## [0.16.0] - 2026-03-10
## [0.50.0] - 2026-03-11
### Added
- **`encrypt_frame`/`decrypt_frame`** — XOR cipher
- **`prepare_migration`/`accept_migration`** — stream handoff
- **`is_frame_duplicate`** — FNV hash dedup
- 3 new tests (54 total)
- **`cipher_encrypt_v50`/`cipher_decrypt_v50`** — XOR encryption
- **`mux_send_v50`/`mux_pack_v50`** — channel multiplexing
- **`pacer_tick_v50`** — frame pacing
- **`cwnd_ack_v50`/`cwnd_loss_v50`** — AIMD congestion
- **`flow_consume_v50`** — token-bucket flow
- **`replay_record_v50`/`replay_count_v50`** — replay recording
- **`shaper_allow_v50`** — bandwidth shaping
- 9 new tests (111 total)
## [0.15.0] — Adaptive quality, metrics snapshot, frame transforms
## [0.14.0] — RLE compress/decompress, sync drift, bandwidth limiting
## [0.13.0] — Replay ring buffer, header serde, codec registry
## [0.40.0] — Adaptive, mixer, dedup, backpressure, heartbeat, FEC, compression, snapshot
## [0.30.0] — Ring buffer, loss detection, quality

View file

@ -1,6 +1,6 @@
[package]
name = "ds-stream-wasm"
version = "0.16.0"
version = "0.50.0"
edition.workspace = true
license.workspace = true
description = "WebAssembly codec for DreamStack bitstream protocol"

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,17 @@
# Changelog
## [0.16.0] - 2026-03-10
## [0.50.0] - 2026-03-11
### Added
- **`FrameEncryptor`** — XOR cipher `encrypt(data, key)` / `decrypt(data, key)`
- **`StreamMigration`** — `prepare_handoff()` / `accept_handoff()` for server transfer
- **`FrameDedup`** — FNV hash dedup with `is_duplicate()`, `dedup_savings()`
- **`PriorityQueue<T>`** — `enqueue(item, priority)`, `dequeue()` highest-first
- 4 new tests (143 total)
- **`StreamCipher`** — XOR encryption with rotating key
- **`ChannelMux`/`ChannelDemux`** — multi-channel mux/demux
- **`FramePacer`** — interval-based frame pacing
- **`CongestionWindow`** — AIMD congestion control
- **`FlowController`** — token-bucket flow control
- **`ProtocolNegotiator`** — capability exchange
- **`ReplayRecorder`** — frame recording for replay
- **`BandwidthShaper`** — enforce max bytes/sec
- 9 new tests (201 total)
## [0.15.0] — AdaptiveBitrate, MetricsSnapshot, FramePipeline
## [0.14.0] — FrameCompressor (RLE), MultiClientSync, BandwidthThrottle
## [0.13.0] — ReplayBuffer, header serde, CodecRegistry
## [0.40.0] — QualityAdapter, SourceMixer, FrameDeduplicator, BackpressureController, HeartbeatMonitor, CompressionTracker, FecEncoder, StreamSnapshot, AdaptivePriorityQueue
## [0.30.0] — FrameRingBuffer, PacketLossDetector, ConnectionQuality

View file

@ -1,6 +1,6 @@
[package]
name = "ds-stream"
version = "0.16.0"
version = "0.50.0"
edition.workspace = true
license.workspace = true
description = "Universal bitstream streaming — any input to any output"

File diff suppressed because it is too large Load diff