Skip to content

Add DevListenServer TCP listener with hello/ping handshake#95

Merged
mblackman merged 1 commit into
mainfrom
dev-listen
Jun 2, 2026
Merged

Add DevListenServer TCP listener with hello/ping handshake#95
mblackman merged 1 commit into
mainfrom
dev-listen

Conversation

@mblackman
Copy link
Copy Markdown
Owner

Summary

Stage 6 PR-A of ai/EditorBuildAndDeployPlan.md — foundation for the dev-iterate loop. TCP listener the editor (and other tooling) will dial into for asset / script push and Lua eval. This PR ships the socket + thread infrastructure and the hello + ping ops; PR-B will land eval_lua / reload_scene / push_script / push_asset.

  • New src/Dev/DevListenServer.{h,cpp} — Registry singleton, listener thread bound to 127.0.0.1:<port> (or 0.0.0.0 with --dev-listen-all). Compile-stripped from ship builds via #ifndef OCTARINE_SHIPPED on header / .cpp / CMake / Game / Main.
  • Wire v1: little-endian u32 length | u32 op | body[length-4].
    • Hello returns a versioned banner string so clients can gate on protocol version.
    • Ping echoes the client's nonce body back.
    • Reserved op ids for PR-B (10 EvalLua, 11 ReloadScene, 12 PushScript, 13 PushAsset) noted in code so we don't reuse them.
  • BSD sockets on POSIX, Winsock on Windows (ws2_32 linked, WSAStartup refcounted so tests + engine share one init).
  • CLI: --dev-listen <port> opts in; --dev-listen-all flips the bind to 0.0.0.0 with a loud Logger::Warn. Both flags log a "stripped by shipped build" warning when seen in a shipped binary and parse no-op so they cannot eat a CLI slot.
  • Pump() runs on the main thread per frame; PR-A has nothing to drain through it. PR-B will SPSC the not-thread-safe ops (Lua eval, scene reload, asset writes) through it.

Out of scope (PR-B + later):

  • Real ops (eval_lua / reload_scene / push_script / push_asset)
  • JSON dep (PR-A wire stays binary; PR-B can pull nlohmann-json if metadata needs it)
  • Editor-side client (Devices panel from Stage 8)

Test plan

  • cmake --preset editor-debug -DOCTARINE_ENABLE_TESTS=ON && cmake --build build/editor-debug --target OctarineDevListenServerTest — clean compile on Windows. ws2_32 links.
  • ctest -R DevListenServerTest — green locally (0.01s). Asserts: Start on ephemeral port, Hello banner roundtrip, Ping echoes nonce, Stop joins thread cleanly, Stop+Start cycle clean.
  • Editor-debug + ship-release symbol surface: shipped build does not link ws2_32 (CMake guard) and does not compile the .cpp (SRC_FILES guard).
  • Manual: launch editor with --dev-listen 9001, dial in with python -c "import socket,struct; s=socket.create_connection(('127.0.0.1',9001)); s.send(struct.pack('<II',4,1)); print(s.recv(256))", observe banner.
  • CI: cross-platform legs prove POSIX-socket path compiles on Linux + macOS and the test runs on each.

mblackman added a commit that referenced this pull request May 31, 2026
POSIX recv/send return ssize_t and take size_t; Winsock returns int and takes
int. -Werror=conversion on the linux/macos legs flagged the implicit ssize_t
-> int narrowing in the engine source and would flag the test too. Introduce
io_size_t / io_ssize_t typedefs picked per-platform so the same call shape
compiles cleanly under both.

Fixes the linux (editor-release) and macos (editor-release) CI failures on
PR #95.
Stage 6 PR-A of ai/EditorBuildAndDeployPlan.md — foundation for the
dev-iterate loop. TCP listener the editor (and other tooling) dials into;
ships the socket + thread infrastructure plus the hello + ping ops. PR-B
will land eval_lua / reload_scene / push_script / push_asset.

- New src/Dev/DevListenServer.{h,cpp}; bound to 127.0.0.1:<port>
  (--dev-listen) or 0.0.0.0 (--dev-listen-all). Compile-stripped from
  shipped builds via OCTARINE_SHIPPED on header / .cpp / CMake / Game / Main.
- Wired into the per-layer engine split: Dev/DevListenServer.cpp joins
  octarine_engine (non-shipped only) and ws2_32 is linked there on Windows.
- Listener uses a non-blocking accept gated by select() with a 100ms
  timeout; Stop() flips a stop flag, joins, then closes the socket. Closing
  the listen fd does NOT wake a blocked accept() on Linux, so the prior
  close-before-join hung the join() forever (6h CI timeout on the
  editor-release leg); the select loop has no blocking accept to wake.
  Accepted client sockets are forced back to blocking so ReadAll/WriteAll
  keep their >0 / <=0 semantics (Windows-accepted sockets inherit the
  listener's non-blocking flag).

Verified: editor-debug + editor-release (-Werror) build clean; ctest
DevListenServerTest green, 25/25 release runs with no hang.
@mblackman mblackman merged commit fd9c188 into main Jun 2, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant