The Multiplayer ARPG in Your Browser
A built world, a database that remembers, a server that answers, a websocket that pushes — and both your players in the same tab.
42 seconds · English captions included · 8.5 Mo
The 7 things you'll know how to do
Lesson 12: The Multiplayer ARPG
Regarde la leçon (42 s) : un monde bâti, une base qui se souvient, un serveur qui répond, un websocket qui pousse — quatre étapes, et ton ARPG tourne dans un onglet.
Build the world, cell by cell
Écris `cellule(x, y, sol)` : une case du monde — un Hash avec `"x"`, `"y"` et `"sol"` (ex: `"herbe"`). Le monde 8-bit commence par UNE case.
The database that remembers
Écris `charge(rows, id)` : lire la base — `rows` est un tableau de lignes Hash (avec `"id"`), renvoie LA ligne dont l'id vaut `id`, sinon `nil`. Sans ce lire, la partie recommence à zéro.
The server answers routes
Écris `route(chemin)` : le petit routeur — `"/joueurs"` → `:joueurs` · `"/monde"` → `:monde` · `"/parties"` → `:parties` · tout autre chemin → `nil`. Le serveur ne fait QUE ça : un chemin, un symbole.
The websocket pushes the moves
Écris `evenement(msg)` : le serveur reçoit `msg` (un Hash avec `"type"`) — `"tire"` → `:inflige` · `"parle"` → `:diffuse` · tout autre type → `:ignore`. Le websocket ne fait que ça : type → action.
Deliver to the RIGHT actor
Écris `remets?(element, acteur)` : un élément venu de la base porte `"pour"` (un id) et `"role"` (`"pnj"` ou `"joueur"`) ; l'acteur porte `"id"` et `"role"`. true SEULEMENT si l'id correspond ET le rôle correspond — au bon joueur, au bon PNJ.
Lesson 12: OK
Sans aide : `transfert(rows, id, acteur)` — 1) cherche la ligne `id` dans la base `rows` (absente → `false`) · 2) livre seulement si `"pour"` == `"id"` de l'acteur ET `"role"` == `"role"` de l'acteur. Le reste, c'est `false`.
📄 The cheat sheet
print it, stick it on your monitor, pretend it was your idea📄 Cheat sheet — The Multiplayer ARPG in your browser (world · database · flow · websocket)
An 8-bit multiplayer ARPG in a tab is four moves: build the world, remember it in a database, answer calls on routes, push events over websocket — then deliver every element to the RIGHT actor.
The 4 moves of the journey
cell → database → route → websocket (then: delivery to the right actor)
cellule() charge() route() evenement()
1. The world: cells, not an engine
def cellule(x, y, sol)
{ "x" => x, "y" => y, "sol" => sol }
end
cellule(2, 5, "herbe") # => {"x"=>2, "y"=>5, "sol"=>"herbe"}
The whole world = an array of these hashes (lessons 6 + 8, revisited). The tileset comes later — data first.
2. Memory: the database that remembers
def charge(rows, id)
rows.find { |r| r["id"] == id } # the row… or nil
end
Between two sessions, the database is the only thing that survives the tab. You load, you play, you save: three calls, and the world has a memory.
3. The flow: one path, one symbol
case chemin
when "/joueurs" then :joueurs
when "/monde" then :monde
when "/parties" then :parties
else nil # unknown: the server says no
end
A request is nothing else: the client shouts a path, the server answers with a symbol
(or nil — an honest 404).
4. The websocket: type → action, pushed
case msg["type"]
when "tire" then :inflige # the server applies…
when "parle" then :diffuse # …and BROADCASTS to the others
else :ignore # not gameplay: we bin it
end
The difference with the route: the server speaks first. The socket stays open, the moves go to everyone in real time.
5. Delivery: to the right actor, never "almost"
def remets?(element, acteur)
element["pour"] == acteur["id"] && element["role"] == acteur["role"]
end
An element from the database carries its destination (pour) and its audience (role:
"pnj" or "joueur"). Both conditions — id alone delivers to the wrong NPC, role alone to
anyone. That's the multiplayer sorting &&.
The full turn (what your server does per message)
def transfert(rows, id, acteur)
el = rows.find { |r| r["id"] == id } # 1. the database
return false unless el # 2. nothing found → nothing leaves
el["pour"] == acteur["id"] && el["role"] == acteur["role"] # 3. right recipient
end
Database → lookup → double condition → delivered. Five lines of Ruby for a whole MMO backend. (The real one adds persistence, secret ids, latency — but the shape is this.)
Never say again
"you need a game engine for multiplayer"→ a database, a route, a socket"the server decides everything"→ it decides WHAT IS TRUE: the database says who has what"websocket = real-time magic"→ acase/whenon messages, pushed in a loop
SlopCourse — from slop to clean code.