A real-time strategy game built for Roblox — capture territory, manage economy, and command troops across a procedurally generated map against up to 4 teams. Built with Rojo and Luau.
A few decisions in this codebase worth calling out:
- Fog of war is computed per-player on the server, not hidden client-side.
MapGeneration.SerializeForPlayer()runs a BFS from each player's owned nodes and buckets the map into three tiers — full info (owned + 1 hop), structure-only "ghost" nodes (2–4 hops), and nodes never sent to the client at all. A client simply cannot know what it hasn't been told, closing off an entire class of cheats that plague client-side fog implementations. - Networking is batched and event-typed by design.
NetworkBatch.luauis a shared module used by both server and client: the server queues updates byEventType(MapUpdate,TroopMovement,GameState, ...) and flushes them in a singleRemoteEventfiring per player per frame, instead of firing a remote per change. The client registers one callback per event type and stays decoupled from how updates are batched. - A dependency-injected module loader instead of a service locator grab-bag.
Packager.luauregisters every module, then runs a strict two-phase boot: everyInit()runs (wiring references) before anyStart()runs (starting loops/connections) — so no module can accidentally depend on another module's loop already being live. - A pathfinding runs over a graph, not a grid.*
AStar.luaufinds shortest paths across the procedurally generated node graph using Euclidean distance plus a 1.1x-weighted heuristic, with an early-exit for directly connected nodes anddebug.profilebegin/profileendinstrumentation to keep pathing cheap during large battles. - Combat and army speed are simulated continuously, not resolved in discrete ticks. Contested nodes lose troops proportional to enemy strength every frame (
damage = enemyTroops * COMBAT_DAMAGE_RATE * deltaTime), and marching armies slow down as they get larger (TroopConfig.GetTroopSpeed), so a large army is powerful but slower to project — a tradeoff baked into the simulation rather than a UI rule. - Server is the sole authority; the client only renders. Every mutation — building placement, troop orders, ownership changes — happens in
src/server, is serialized, and pushed down.src/clientmodules (MapRenderer,TroopRenderer,BuildingRenderer) exist purely to turn that state into parts, GUIs, and animations, which keeps the game resistant to client-side exploiting.
- Boot.
Server.server.luauandClient.client.luauload their respective modules throughPackager, which wires dependencies (Init) and then starts loops/connections (Start). - Game loop.
ServerModule.luaudrives the match: wait for players → generate a fresh map (MapGeneration.luau) → place spawns and assign teams → run per-frameHeartbeatloops for combat, production, troop movement, and networking → detect a team's capital falling → declare a winner and restart. - Territory & economy.
Team.luautracks node ownership and per-team troop counts;Buildings.luaulets players spend troops to build Factories (troop production) and Powerplants (production boost to connected buildings) on owned nodes. - Commands.
UserInputService.luaucaptures drag-select and hotkeys (send 10/50/100/all troops, build, retreat) and forwards them throughClientController.luauto server-sideRemoteEvents, whichTroops.luauandBuildings.luauvalidate and apply. - Movement. Troops sent between non-adjacent nodes get routed by
AStar.FindPathand then walked segment-by-segment each frame inTroops.UpdateMovingTroops, including head-on collision handling where the smaller army is forced to retreat. - Sync. Every frame, the server serializes each player's fog-of-war-filtered view and pushes it down via
NetworkBatch; the client'sMapRenderer/TroopRenderer/BuildingRendererreconcile the visuals against that state.
See docs/Architecture.md for the full module dependency graph and network/data flow, docs/API.md for every module's public API, and docs/DataStructures.md for the exact shape of every data structure and network payload.
| Layer | Choice |
|---|---|
| Runtime | Roblox / Luau |
| Project sync | Rojo 7.7.0-rc.1 |
| Toolchain management | Aftman |
| Networking | Custom batched RemoteEvent layer (NetworkBatch.luau) |
| Module system | Custom two-phase loader (Packager.luau) |
| Pathfinding | Custom A* over a procedural node graph (AStar.luau) |
Install Aftman to pick up the pinned Rojo version, then:
aftman installTo build the place from scratch:
rojo build -o "RiskyStrats.rbxlx"Then open the built file in Roblox Studio and start the Rojo server so Studio stays in sync with your edits:
rojo serveFor more on the Rojo workflow, see the Rojo documentation.
| Input | Action |
|---|---|
| Left Click / Drag | Select nodes (circle select) |
| Shift + Click | Add to selection |
| Alt + Click | Retreat troops |
| Q / E / R | Send 10 / 50 / 100 troops |
| F | Send all troops |
| 1 | Build Factory |
| 3 | Build Powerplant |
src/
Packager.luau # Two-phase module loader (Init then Start)
Server.server.luau # Server entry point
Client.client.luau # Client entry point
server/
ServerModule.luau # Main game loop (waiting -> playing -> cleanup)
MapGeneration.luau # Procedural map generation, fog-of-war serialization
Team.luau # Node ownership, per-team troop counts, elimination
Troops.luau # Troop orders, movement, combat resolution
Buildings.luau # Building placement + production
AStar.luau # Pathfinding over the node graph
Player.luau / PlayerData.luau # Player-to-team assignment, persistence
TestMapCycle.luau # Map generation test harness
client/
ClientController.luau # Selection, commands, cached state
ClientModule.luau # Client bootstrap, network callback registration
MapRenderer.luau # Node/road visuals
TroopRenderer.luau # Moving troop visuals
BuildingRenderer.luau # Building visuals
UserInputService.luau # Mouse/keyboard bindings
VotingClient.luau # Pre-game map/gamemode voting UI
shared/
NetworkBatch.luau # Batched, event-typed RemoteEvent layer
BuildingConfig.luau # Building stats (cost, production, boost)
TroopConfig.luau # Troop speed-by-size curve
TeamColors.luau # Team color palette
docs/
Architecture.md # Module relationship map, data/network flow
API.md # Full public API per module
DataStructures.md # Exact shape of every data type + network payload