User Tools

Site Tools


moss:initial_notes_for_v0.1

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
moss:initial_notes_for_v0.1 [2026/06/26 10:06] appledogmoss:initial_notes_for_v0.1 [2026/06/26 10:21] (current) appledog
Line 13: Line 13:
 So Mossworld is split into **two cooperating programs**: So Mossworld is split into **two cooperating programs**:
  
-* **The client** — plain HTML/CSS/JavaScript running in each player's browser. It draws the terminal (a scrolling output area plus a one-line input) and does nothing clever: it sends whatever you type, and prints whatever it receives. +* **The client** is a plain HTML/CSS/JavaScript running in each player's browser. It draws the terminal (a scrolling output area plus a one-line input) and does nothing clever: it sends whatever you type, and prints whatever it receives. 
-* **The server (the "daemon")** — a single long-lived PHP program running on the host. It holds the whole world in memory, knows every connected player, and pushes messages out to them the instant something happens.+* **The server (the "daemon")** is a single long-lived PHP program running on the host. It holds the whole world in memory, knows every connected player, and pushes messages out to them the instant something happens.
  
 {{{ {{{
Line 29: Line 29:
 }}} }}}
  
-The clean rule we follow: **the server owns the world; the client owns the display.** Colours, for example, are 100% a client concern — the ''colormode'' command never even reaches the server.+The clean rule we follow: **the server owns the world; the client owns the display.** Colours, for example, are 100% a client concern. The ''colormode'' command never even reaches the server.
  
 == What is a WebSocket? == What is a WebSocket?
Line 36: Line 36:
 A **WebSocket** is a connection that **stays open** in both directions. After a brief handshake (which starts life as an ordinary HTTPS request and then "upgrades"), the browser and server hold a persistent two-way pipe. Either side can send a short text message at any moment, with almost no overhead. A **WebSocket** is a connection that **stays open** in both directions. After a brief handshake (which starts life as an ordinary HTTPS request and then "upgrades"), the browser and server hold a persistent two-way pipe. Either side can send a short text message at any moment, with almost no overhead.
  
-For Mossworld this is exactly right: +This allows push messages:
 * You press Enter and the client sends one line (e.g. ''east'') down the pipe. * You press Enter and the client sends one line (e.g. ''east'') down the pipe.
-* The server moves you, and **pushes** "Appledog leaves east." to everyone else in the room — without them asking. That push is the thing plain HTTP can't do.+* The server moves you, and **pushes** "Appledog leaves east." to everyone else in the room without them asking. That push is the thing plain HTTP can't do.
  
-The secure form is <code>wss://</code> (WebSocket-over-TLS), the encrypted sibling of ''https://''. Browsers refuse an insecure ''ws://'' connection from an ''https://'' page, so we always use ''wss://''.+The secure form is wss:%%//%% (WebSocket-over-TLS), the encrypted sibling of ''https:%%//%%''. Browsers refuse an insecure ''ws:%%//%%'' connection from an ''https:%%//%%'' page, so we always use ''wss:%%//%%''.
  
 == What is a "daemon"? == What is a "daemon"?
Line 65: Line 64:
  
 == The world engine == The world engine
-The actual game logic — what rooms exist, what ''east'' does, what you "see" lives in **Engine** (''php/Engine.php''). It is deliberately **transport-agnostic**: it knows nothing about WebSockets, browsers, or networking. You hand it ''(context, "east")'' and it returns text. That separation means we could drive the exact same engine from a command-line test, an old-style telnet server, or the WebSocket daemon; only the delivery changes.+The actual game logic (what rooms exist, what ''east'' does, what you "see"lives in **Engine** (''php/Engine.php''). It is deliberately **transport-agnostic**: it knows nothing about WebSockets, browsers, or networking. You hand it ''(context, "east")'' and it returns text. That separation means we could drive the exact same engine from a command-line test, an old-style telnet server, or the WebSocket daemon; only the delivery changes.
  
 The proof-of-concept world was a 2×2 square of four rooms. Exits are stored as four columns on each room (''n_to'', ''s_to'', ''e_to'', ''w_to''), each holding the destination room id (0 = no exit). Dead simple to start; when we want stairs, portals, or locked doors we'll graduate to a proper exits table. The proof-of-concept world was a 2×2 square of four rooms. Exits are stored as four columns on each room (''n_to'', ''s_to'', ''e_to'', ''w_to''), each holding the destination room id (0 = no exit). Dead simple to start; when we want stairs, portals, or locked doors we'll graduate to a proper exits table.
  
-The whole map is loaded into memory **once** when the server starts, so moving around is a memory lookup — it never touches the database. The database is for //durable// things (accounts), not for the per-keystroke game loop.+The whole map is loaded into memory **once** when the server starts, so moving around is a memory lookupit never touches the database. The database is for //durable// things (accounts), not for the per-keystroke game loop.
  
 == Authentication: bridging the web login to the live game == Authentication: bridging the web login to the live game
Line 76: Line 75:
 We have two separate worlds of identity: We have two separate worlds of identity:
 * The **web side** is a classic PHP site (login form, ''$_SESSION'', a cookie). This is well-understood and where account signup/login lives. * The **web side** is a classic PHP site (login form, ''$_SESSION'', a cookie). This is well-understood and where account signup/login lives.
-* The **daemon** is a different process entirely. It does **not** share PHP's sessions or cookies — it just has open sockets. So when a browser opens a WebSocket, the daemon has no idea //who// that is.+* The **daemon** is a different process entirely. It does **not** share PHP's sessions or cookiesit just has open sockets. So when a browser opens a WebSocket, the daemon has no idea //who// that is.
  
 The bridge is a one-time **ticket**: The bridge is a one-time **ticket**:
-# You log in on the web → a normal PHP session marks you as authenticated. +# You log in on the web and the PHP session marks you as authenticated. 
-# When the game page (''index.php'') loads, it checks that session. No session → you are redirected to the sign-in page (**there are no guests**).+# When the game page (''index.php'') loads, it checks that session. No session and you are redirected to the sign-in page (//"there are no guests"//).
 # If you //are// logged in, the page mints a random **ticket**, stores it in a ''tickets'' table (your uid + a timestamp), and embeds it in the page. # If you //are// logged in, the page mints a random **ticket**, stores it in a ''tickets'' table (your uid + a timestamp), and embeds it in the page.
 # The browser opens the WebSocket and **its very first message is** ''auth <ticket>''. # The browser opens the WebSocket and **its very first message is** ''auth <ticket>''.
Line 99: Line 98:
  
 == Security choices == Security choices
-* **Source code is not web-served.** The folders ''db/'', ''php/'', and ''server/'' each carry a ''.htaccess'' that denies all web access. PHP still ''include''s those files from disk (that's a filesystem read, unaffected), but a browser asking for them directly gets 403 — so the DB password, the engine, etc. are never downloadable. ''sec/'' and ''admin/'' //are// web-served, because they are real pages.+* **Source code is not web-served.** The folders ''db/'', ''php/'', and ''server/'' each carry a ''.htaccess'' that denies all web access. PHP still ''include''s those files from disk (that's a filesystem read, unaffected), but a browser asking for them directly gets 403 so the DB password, the engine, etc. are never downloadable. ''sec/'' and ''admin/'' //are// web-served, because they are real pages.
 * **Passwords** are hashed with **Argon2id** (a modern, deliberately-slow hash). We never store or compare plaintext. * **Passwords** are hashed with **Argon2id** (a modern, deliberately-slow hash). We never store or compare plaintext.
-* **Privileges** use a ''roles'' table (named roles like ''admin'') rather than a single "security level" number — more granular and practical.+* **Privileges** use a ''roles'' table (named roles like ''admin'') rather than a single "security level" number. More granular and practical.
 * **Signup** is protected with a CSRF token; usernames are restricted so the account name is also a clean in-game name. * **Signup** is protected with a CSRF token; usernames are restricted so the account name is also a clean in-game name.
 * **The WebSocket ticket** is short-lived and tied to your account, so an open socket can't impersonate someone. * **The WebSocket ticket** is short-lived and tied to your account, so an open socket can't impersonate someone.
Line 107: Line 106:
 == Deployment Protocol == Deployment Protocol
 * ''deploy.sh'' pushes the whole tree to the live server in **one rsync** over SSH (fast, delta-only), skipping editor/tooling files. * ''deploy.sh'' pushes the whole tree to the live server in **one rsync** over SSH (fast, delta-only), skipping editor/tooling files.
-* The daemon runs unprivileged on ''127.0.0.1:2346'' speaking plain ''ws://''. **Apache** sits in front, terminates TLS using the real certificate, and proxies ''wss://helloneo.ca/mossworld/ws'' through to the local daemon. This way the daemon needs no privileges and never touches the private key. +* The daemon runs unprivileged on ''127.0.0.1:2346'' speaking plain ''ws:%%//%%''. **Apache** sits in front, terminates TLS using the real certificate, and proxies ''wss:%%//%%helloneo.ca/mossworld/ws'' through to the local daemon. This way the daemon needs no privileges and never touches the private key. 
-* ''server/moss-ctl.sh'' starts/stops/restarts the daemon. A **restart is required after changing daemon code** — a long-running program holds its code in memory, unlike normal per-request PHP. +* ''server/moss-ctl.sh'' starts/stops/restarts the daemon. A **restart is required after changing daemon code.**
  
 == File-by-file tour == File-by-file tour
 === Top level === Top level
-* ''index.php'' — the **game client**. Top half is PHP: require login, mint the WebSocket ticket. Bottom half is the browser terminal (scrolling output + input box), the WebSocket connect/reconnect logic, the colour profiles, and the control-frame handling. This is the only game file a browser loads directly. +* ''index.php'' -- the **game client**. Top half is PHP: require login, mint the WebSocket ticket. Bottom half is the browser terminal (scrolling output + input box), the WebSocket connect/reconnect logic, the colour profiles, and the control-frame handling. This is the only game file a browser loads directly. 
-* ''deploy.sh'' — one-pass rsync deploy to the live host. +* ''deploy.sh'' -- one-pass rsync deploy to the live host. 
-* ''composer.json'' — declares the Workerman dependency (installed into ''vendor/'' on the server, which is not committed). +* ''composer.json'' -- declares the Workerman dependency (installed into ''vendor/'' on the server, which is not committed). 
-* ''movetest.php'' — a tiny command-line harness that drives the Engine with no network, to prove movement logic in isolation.+* ''movetest.php'' -- a tiny command-line harness that drives the Engine with no network, to prove movement logic in isolation.
  
 === db/  (web-locked; durable data) === db/  (web-locked; durable data)
-* ''mdb.php'' — **MeekroDB**, a small library wrapping MySQL with safe parameterised queries (''DB::query(...)''). Also holds the DB credentials. +* ''mdb.php'' -- **MeekroDB**, a small library wrapping MySQL with safe parameterised queries (''DB::query(...)''). Also holds the DB credentials. 
-* ''schema.php'' — every table definition as a ''create_table_*()'' function: ''rooms'', ''traffic'', ''members'' (accounts), ''roles'', ''tickets''+* ''schema.php'' -- every table definition as a ''create_table_*()'' function: ''rooms'', ''traffic'', ''members'' (accounts), ''roles'', ''tickets''
-* ''install.php'' — command-line installer: (re)creates/seeds the tables. +* ''install.php'' -- command-line installer: (re)creates/seeds the tables. 
-* ''User.php'' — the **account** class: load by id, check username/email, verify Argon2id password, remember-me key, and the role methods (''has_role'', ''add_role'', ''is_admin''). +* ''User.php'' -- the **account** class: load by id, check username/email, verify Argon2id password, remember-me key, and the role methods (''has_role'', ''add_role'', ''is_admin''). 
-* ''.htaccess'' — denies all web access to this folder.+* ''.htaccess'' -- denies all web access to this folder.
 * ''registry.php'', ''iplog.php'', ''roles.php'' are legacy helpers carried over from an earlier project, currently unused (kept as reference). * ''registry.php'', ''iplog.php'', ''roles.php'' are legacy helpers carried over from an earlier project, currently unused (kept as reference).
  
 === php/  (web-locked; shared code) === php/  (web-locked; shared code)
-* ''Engine.php'' — the transport-agnostic **world engine**: loads rooms, renders a room, processes movement/look commands. +* ''Engine.php'' -- the transport-agnostic **world engine**: loads rooms, renders a room, processes movement/look commands. 
-* ''Context.php'' — the per-player **state bundle** described above. +* ''Context.php'' -- the per-player **state bundle** described above. 
-* ''globalvars.php'' — site constants (host, app base path) and URL helpers. +* ''globalvars.php'' -- site constants (host, app base path) and URL helpers. 
-* ''cregistry.php'' — cookie helpers (prefixed, Secure, HttpOnly, path-scoped). +* ''cregistry.php'' -- cookie helpers (prefixed, Secure, HttpOnly, path-scoped). 
-* ''ncrypt.php'' — a small random-token generator (utility). +* ''ncrypt.php'' -- a small random-token generator (utility). 
-* ''.htaccess'' — denies all web access.+* ''.htaccess'' -- denies all web access.
  
 === server/  (web-locked; the daemon) === server/  (web-locked; the daemon)
-* ''moss-server.php'' — the **WebSocket daemon**. Holds the player list, the ''tx()'' metering helper, the control/assist/broadcast helpers, the auth gate and ticket validation, the one-session-per-user kick, and the connect / message / disconnect handlers. This is the live heart of the game. +* ''moss-server.php'' -- the **WebSocket daemon**. Holds the player list, the ''tx()'' metering helper, the control/assist/broadcast helpers, the auth gate and ticket validation, the one-session-per-user kick, and the connect / message / disconnect handlers. This is the live heart of the game. 
-* ''moss-ctl.sh'' — start / stop / restart / status wrapper (with a force-kill fallback so a stuck server can always be replaced). +* ''moss-ctl.sh'' -- start / stop / restart / status wrapper (with a force-kill fallback so a stuck server can always be replaced). 
-* ''.htaccess'' — denies all web access.+* ''.htaccess'' -- denies all web access.
  
 === sec/  (web-served; the login surface) === sec/  (web-served; the login surface)
-* ''security.php'' — starts the session and sets ''$user''. Include this and you know who's logged in. +* ''security.php'' -- starts the session and sets ''$user''. Include this and you know who's logged in. 
-* ''chrome.php'' — a slim Bootstrap-5 (dark) page header/nav/footer used **only** by the web pages (login, admin). The game does not use Bootstrap. +* ''chrome.php'' -- a slim Bootstrap-5 (dark) page header/nav/footer used **only** by the web pages (login, admin). The game does not use Bootstrap. 
-* ''sign-in.php'' / ''login.php'' — the login **form** and its **handler**. +* ''sign-in.php'' / ''login.php'' -- the login **form** and its **handler**. 
-* ''new-user-form.php'' / ''create-new-user.php'' — the signup **form** and its +* ''new-user-form.php'' / ''create-new-user.php'' -- the signup **form** and its **handler** (CSRF-checked; on success you're logged in and sent to the game). 
-  **handler** (CSRF-checked; on success you're logged in and sent to the game). +* ''logout.php'' -- clears the session and remember-me key. 
-* ''logout.php'' — clears the session and remember-me key. +* ''bootstrap.php'', ''doing.php'', ''new.php'', ''settings.php'' -- legacy nelsonacademy auth scaffolding, unused (kept for future forums/ticketing).
-* ''bootstrap.php'', ''doing.php'', ''new.php'', ''settings.php'' — legacy nelsonacademy auth scaffolding, unused (kept for future forums/ticketing).+
  
 === admin/ === admin/
-* ''admin.php'' — admin panel **stub**: shows who you're logged in as and whether you hold the ''admin'' role. Real tools will grow here. +* ''admin.php'' -- admin panel **stub**: shows who you're logged in as and whether you hold the ''admin'' role. Real tools will grow here. 
-* ''design-notes.txt'' — this document.+* ''design-notes.txt'' -- this document.
  
 === bootstrap/ and js/ === bootstrap/ and js/
 * Vendored **Bootstrap 5** (CSS/JS) and a couple of jQuery helpers, used only by the web pages. Static assets. * Vendored **Bootstrap 5** (CSS/JS) and a couple of jQuery helpers, used only by the web pages. Static assets.
  
-=== Known gaps and next steps +=== Immediate improvements 
-* No brute-force rate-limiting on login/signup yet.+v0.1 is a proof-of-concept / prototype. It's the basis for the whole system; the structural bones of it all. So there is naturally a lot of room for improvement. 
 +* No rate-limiting on login/signup or on game commands. 
 +* No filters on chat.
 * The WebSocket ticket is reusable within a short TTL (convenient for reconnects; could be hardened to strictly one-time). * The WebSocket ticket is reusable within a short TTL (convenient for reconnects; could be hardened to strictly one-time).
 * Half-open sockets aren't actively reaped yet (a heartbeat/ping would do it). * Half-open sockets aren't actively reaped yet (a heartbeat/ping would do it).
-* The world is a 4-room placeholder; the object model (objectsproperties, verbs, containment) is the real next chapter+ 
-* Legacy files noted above are candidates to remove or adapt.+Lots of other little things are sure to come out under analysis. But as milestones gov0.1 is a big one
 + 
 +<blockquote>"When in doubt, just start coding."</blockquote>
moss/initial_notes_for_v0.1.1782468364.txt.gz · Last modified: by appledog

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki