Sides and environments
Status: Implemented, except the multiplayer handshake for
sync, which is Planned: the field is read and checked byclight check, but connections don’t compare mod lists yet.
Celestial keeps vanilla’s split between the client and the server. Even in singleplayer, the game runs an integrated server in the same process as the client, and the two talk through typed packet queues instead of a socket (ARCHITECTURE §1). A mod therefore always has to know which side it is running on.
Physical and logical sides
| Term | Meaning |
|---|---|
| Physical client | The seecraft executable. It contains the client, and in singleplayer also an integrated server. |
| Physical server | The seecraft_server executable (dedicated server). It has no renderer, audio, GUI or input. |
| Logical client | The code that renders, plays sound, reads input and holds the client’s copy of the world (ClientLevel). It exists only in the physical client. |
| Logical server | The code that owns the authoritative world: ticking, saving, AI and worldgen. It exists in the dedicated server, and inside the physical client while a singleplayer world (or an opened-to-LAN world) is running. |
In singleplayer one process holds both logical sides at once. Your mod receives server events (from the integrated server) and client events. Every event and every function states its side (Complete event list, the generated tables).
bool cl_level_is_client(cl_level level); // true for the client's copy of the world
cl_env cl_environment(void); // CL_ENV_CLIENT (physical client) or CL_ENV_DEDICATED_SERVER
bool cl_server_running(void); // a logical server runs in this process (dedicated or integrated)
| C | C++ | Lua |
|---|---|---|
cl_level_is_client(level) |
level.is_client() |
level:is_client() |
cl_environment() |
clight::mods::environment() |
cl.mod.environment() ("client" or "dedicated_server") |
cl_server_running() |
clight::mods::server_running() |
cl.mod.server_running() |
Rule: world state changes (placing blocks, damaging entities, giving items) happen on the logical server. The client sends input to the server through vanilla packets or through your own payload (Networking) and displays what the server sends back. A client-side cl_level_set_block changes only the local copy, and the server overwrites it on its next update. That can be useful for prediction, but it is never authoritative.
The manifest side field
Every mod declares where its code may load:
side |
Loaded in the physical client | Loaded in seecraft_server |
|---|---|---|
"client" |
yes (client APIs only) | no, skipped silently |
"server" |
yes (on the integrated server only) | yes |
"both" |
yes | yes |
Calling a client-only function (cl_hud_*, cl_screen_*, cl_gui_*, cl_rml_*, cl_keybind_*, cl_sound_play_local, cl_client_*) on a dedicated server returns CL_E_WRONG_SIDE (Lua raises wrong_side). Server-only functions (cl_player_give, cl_cmd_register, cl_menu_open, …) behave the same way on a client without a running server. The functions are still linked everywhere, so one .clmod serves both sides.
Multiplayer requirement: sync
side says where code runs. sync says what a multiplayer connection needs:
sync |
Meaning | Example |
|---|---|---|
"required" |
Both sides need the mod at a compatible version (the handshake that refuses a mismatch is planned). | A client feature that only works with the server half |
"optional" |
Works with or without a counterpart. If the other side has it, extra features turn on. | A server mod with an optional client HUD |
"client_only" |
Never negotiated. Works on any server, including vanilla and older versions through the compat layer. | Minimaps, HUD mods, the Client Settings features |
"server_only" |
Never negotiated. Vanilla clients can join. | Commands, shops, server-side content (mod blocks and items are vanilla-backed, so vanilla clients see their fallbacks) |
See Multiplayer sync and the handshake for what is synced today.
Typical setups
| Setup | Client | Server |
|---|---|---|
| Celestial client with a client-only mod on a vanilla/Paper server | mod (client_only) |
vanilla |
| Celestial client on an older server (1.8–26.2) through the compat layer | mod (client_only or optional) |
old vanilla |
Vanilla client on a seecraft_server with plugins |
vanilla | plugins (server_only) |
| Modded singleplayer | client side and integrated server in one process | |
| Modded multiplayer | mod (required) |
mod (required) |
Checking at runtime
// C++
if (clight::mods::environment() == CL_ENV_CLIENT) {
// client-only setup: keybinds, HUD layers, screens
}
-- Lua
if cl.mod.environment() == "client" then
-- client-only setup
end
The lifecycle has separate lifecycle.client_setup (physical client) and lifecycle.server_setup (dedicated server) events, so most mods never test the side by hand (Mod lifecycle).