Skip to content

Sides and environments

Status: Implemented, except the multiplayer handshake for sync, which is Planned: the field is read and checked by clight 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).