Client and server
Lesson 14.1
Every Minecraft game has two halves, and your mod lives in both. Knowing which half runs which code saves a lot of confusion.
Two halves
- The server owns the world. It moves mobs, grows crops, decides what happens when a block breaks, and saves everything.
- The client is the game window: it draws the world, plays sounds, reads your keyboard and mouse, and shows menus.
On a multiplayer server, the server is another computer and every player has a client. In singleplayer, both halves run inside your game at once: the client and a small built-in server. Your mod is loaded once and gets the events of both.
Which events run where
The event list marks each event:
- Server events, like
player.joinandblock.break, run where the server is. - Client events, like
client.keyandtick.client_post, run in the game window.
| You play… | Server events | Client events |
|---|---|---|
| Singleplayer | yes | yes |
| On a server that has your mod | on the server | in your game |
| On a server without your mod | no | yes |
That last row is why some mods are client-only: a HUD, a keybind or a sound tweak works on any server, even a vanilla one, because it only uses the client half.
Which functions run where
Most functions belong to one half:
| Server only | Client only |
|---|---|
player:give, player:send_message, player:teleport |
cl.hud.register_layer |
world:set_block |
cl.screen.open |
cl.cmd.register, cl.cmd.run |
cl.keybind.register |
cl.menu.open |
cl.sound.play_ui, cl.music.play |
cl.sched.run_server, repeat_server |
cl.sched.run_client |
cl.entity.server_players() |
cl.entity.client_player(), cl.level.client_level() |
Calling one on the wrong side raises a wrong_side error. Reading, like player.pos or world:get_block, works on both.
The client can’t change the world by itself. To ask the server to do something, like “give me a ruby, I pressed R”, the client sends a message to the server. Talking between client and server shows how.
Asking where you are
if cl.has("side.client") then
-- this game has a window: HUDs, keys and screens are possible
end
print(cl.mod.environment()) --> client (or dedicated_server)
print(cl.mod.server_running()) --> true (a world runs in this game)
cl.has("gui") and cl.has("sound") are false on a dedicated server, which has no window.
The usual pattern
Put client code into the lifecycle.client_setup event. It only runs in a game with a window, so a dedicated server skips it, and your mod works in both places:
-- runs everywhere: registrations and server logic
cl.on("lifecycle.register", function()
cl.items.register{ id = "ruby" }
end)
cl.on("player.join", function(event)
event.player:give("my_mod:ruby")
end)
-- runs only where there is a window
cl.on("lifecycle.client_setup", function()
cl.hud.register_layer{
id = "hello",
render = function(g)
g:text("Ruby mod is here", 4, 4, 0xE0115F, true)
end,
}
end)
The manifest
"side" in clight.mod.json decides where your mod is loaded at all:
"both"for mods with items, blocks, commands or other server logic. Most mods."client"for mods that only change the game window: HUDs, keybinds, sounds. They are skipped on dedicated servers."server"for server-only logic, like rules or commands, that players don’t need to install.
Players without your mod
A server with your mod may have players who don’t have it. They still see something sensible:
- Your items and blocks appear as their
vanilla_fallback. - Commands work, because the server runs them.
- Chest menus from
cl.menu.openwork, because they are vanilla menus. - HUDs, screens and keybinds don’t exist for them, since that code runs in the client.
Try it
Your mod should log “server ready” when a world opens and “window ready” once the game’s window is set up. Which events do you use, and in which of the three situations above does each line appear?
Show a solution
cl.on("lifecycle.server_started", function()
cl.log("server ready")
end)
cl.on("lifecycle.client_setup", function()
cl.log("window ready")
end)
In singleplayer you see both lines (the window one at startup, the server one when you open a world). On a server with the mod, the server’s log has “server ready” and your game’s log has “window ready”. On a server without the mod, only your game’s “window ready” appears.
Recap: the server owns the world and the client draws it; server events and functions only run where the world is simulated, and client code goes into lifecycle.client_setup.