Planets
Example: Planet System - the planet Ember with a heightmap, ground layers, a sea, clouds and stormy weather, and its airless moon Cinder added from a script.
How it works
A map can bring its own planets instead of using one of the game's worlds. You describe them as data - in the map file's "system" section, or from a script while the mod loads - and every player's game builds the same planets from that description. Nothing about a planet is sent over the network; it does not have to be, because the server and every player have the same mod files (the game checks them) and the same description (the game checks that too, see Multiplayer).
Put the map on the Empty base. A map with a "system" then loads Void, empty space with a sun, and the game builds your planets into it. (A game version without the Void scene loads Flat Planet and hides its planet instead; the result is the same.) On another base (Earth, ...) your planets are added and the base's own planets are hidden, unless you set "replaceBasePlanets": false.
{
"id": "twin-worlds",
"name": "Twin Worlds",
"base": "Empty",
"spawns": [ { "position": [0, 900, 0] } ],
"system": {
"sun": { "color": "#fff1dc", "intensity": 1.0, "pitch": 35, "yaw": 140 },
"weather": "stormy",
"bodies": [
{
"name": "Ember",
"radius": 40000,
"position": [0, -40000, 0],
"gravity": 7.5,
"atmosphere": { "height": 6000 },
"sea": { "level": 0 },
"terrain": {
"heightmap": "/textures/ember-height.png",
"heightRange": 1600,
"midpoint": 0.45,
"ground": { "default": "dirt", "rockSlope": 36 },
"biomes": [
{ "textures": [
{ "albedo": "/textures/grass.png", "ground": "grass", "maxSlope": 28 },
{ "albedo": "/textures/rock.png", "ground": "rock", "minSlope": 30 }
] }
]
},
"clouds": { "bottom": 900, "top": 2700, "coverage": "/textures/clouds.png" }
}
]
}
}Positions are absolute world metres: a planet of radius 40 000 m centred at [0, -40000, 0] has its top at the world's origin. Put the spawn points above the ground there (a missing spawn defaults to just above the top of the first planet). Paths starting with / are from your mod's folder.
system
| Field | Meaning |
|---|---|
bodies | The planets, 1 to 8. Required. |
sun | color, intensity (0-20), pitch (degrees above the horizon) and yaw. Left out: the base scene's sun. |
weather | A preset - "temperate", "arid", "stormy", "frozen", "clear" - or { "preset": ..., fields } with any of enabled, mode (auto/clear/rain/snow/storm), forcedIntensity, hoursAhead, stormCount, fairSpells, stormSize, wetness, windScale, temperatureOffset, equatorTemperature, poleTemperature, lapseRate, minimumAirDensity. The server applies it for everyone when the map starts and puts the previous weather back when the map is left. Left out: the server's weather settings stay as they are. |
replaceBasePlanets | true (default) hides the base map's own planets. Always true on Empty/Void. |
Weather is one setting for the whole world (every planet with air has storms worked out from it, each planet differently because its name seeds them), so it belongs to the system, not to a planet.
A body
| Field | Meaning |
|---|---|
name | Unique, 1-48 letters, digits, spaces and - _ . '. It also seeds the planet's weather, so renaming a planet changes its storms. |
radius | Metres, 1 000 to 10 000 000 (Earth's map is 637 100). |
position | Centre, [x, y, z], absolute metres. Required. Planets (with their air) must not overlap. |
rotation | [x, y, z] degrees. Left out: a tilt chosen so the top of the planet is not on a seam of the terrain mesh. |
gravity | Surface gravity, m/s² (default 9.81). |
gravityReach | Where gravity has faded to nothing, in planet radii (default 3, 0 = never). |
atmosphere | { height, density, falloff, rayleigh, mie } - air height in metres, surface density kg/m³ (1.225 = Earth), how fast it thins (0.5-10), the sky's colour ("#3a6bff") and haze strength - or false for no air and no sky. Left out: Earth-like air 2 % of the radius high. |
sea | { "level": metres } above the radius, true (level 0), or left out for no sea. Only one planet per map has a sea you can float on; on others it is drawn only. |
terrain | See below. |
clouds | { bottom, top, density, coverage } - metres above the radius and a cover picture (grey PNG, equirectangular, white = cloud) - true for defaults, or left out for none. Needs air. |
rings, fields | Not built by this version yet (a warning says so). |
terrain
| Field | Meaning |
|---|---|
heightmap | A grey PNG covering the whole planet in equirectangular projection (2:1, left edge = right edge). Any PNG works - 8 or 16 bits, colour or grey (the red channel is used); the game converts it to the 16-bit format the terrain needs. 16-bit PNGs give smoother slopes. |
heightRange, midpoint | The ground is heightRange × (pixel − midpoint) metres above the radius (pixel 0..1). Sea level is the radius plus sea.level. |
strata | Terracing of the relief (0-10, default 1). |
textureSize | 256, 512, 1024 (default) or 2048: every ground texture and layer heightmap is resized to this square size. |
ground | The physics of the ground where no texture layer says otherwise: default kind, rockSlope (degrees above which it is rock, 90 = never), snowAltitude (metres, 0 = no snow line), shoreHeight (metres of beach above the sea). |
biomes | Up to 4. Each has an optional mask (grey equirectangular PNG, white = this biome; maskInvert, maskSharpness), up to 4 layers of extra relief (heightmap tiled every globalSize metres, range, midpoint, strata) and up to 8 textures (16 per planet). |
Texture layers
{ "albedo": "/textures/rock.png", "ground": "rock", "minSlope": 30, "minSlopeFalloff": 6, "height": "/textures/rock-height.png", "heightMetres": 0.2 }albedo is a colour PNG that tiles. Rules decide where it is painted: minHeight/maxHeight (metres above the radius) and minSlope/maxSlope (degrees), each with a ...Falloff; a later layer paints over earlier ones. height is an optional grey relief picture for close-up depth (heightMetres deep).
ground says what the layer IS for wheels, tracks and digging: dirt, grass, mud, sand, rock, gravel, snow, pavement, bog, or default (the planet's ground rules decide). The game never guesses from a file name for mod planets, so always set it - a layer without it gets a warning.
From a script
planets.system(map, spec) sets a map's whole system and planets.define(map, body) adds one planet to it (to the file's system, or to the one planets.system set). They take the same fields as the file, with the same checks, and work only while your scripts first run - the world is decided before the map is built:
planets.define("twin-worlds", {
name = "Cinder", radius = 9000, position = { 0, 61000, 52000 }, gravity = 1.6, atmosphere = false,
terrain = { ground = { default = "gravel" } },
})Call them from a shared script ("entry": { "shared": "scripts/shared.lua" } in mod.json) so the server and every player run them, and only with values every machine computes the same way - never math.random, the clock or a player's own settings. Paths in scripts are from the mod's folder.
At run time
| Function | Side | Returns |
|---|---|---|
planets.list() | both | { name, radius, position, gravity, gravityReach, atmosphereHeight, airDensity } for every planet of the current map |
planets.nearest(position) | both | the planet whose surface is nearest, or nil |
planets.gravityAt(position) | both | gravity there, m/s² (a vector pointing down) |
planets.airAt(position) | both | air density there, kg/m³ |
planets.weather(nameOrPosition) | both | { planet, rain, snow, temperature, cloud, wind } or nil |
planets.setWeather(presetOrTable) / trySetWeather | server, world permission | changes the weather for everyone until the map is left |
planets.resetWeather() | server, world permission | the weather from before the map |
Multiplayer
The server sends each joining player the identity of the map's planets: a hash of the system (in a fixed text form, so the order of fields and how numbers are written do not matter), the base scene it was built on and the mod's content hash (which already covers every picture). A player whose game built something different - an older version of the mod, a script that set the system on the server only, a different game version - is disconnected with a message rather than left standing on ground the server does not have.
What a mod can and cannot change
You can choose: how many planets (up to 8), their size, place, tilt, gravity and its reach, air (height, density, thinning, sky colour, haze), a sea level, the relief (a whole-planet heightmap plus tiled relief layers in up to 4 masked biomes), painted ground with its rules and its physical kind, the ground rules (default kind, rock slope, snow line, beach), clouds (height, density, cover picture), the sun's colour, brightness and direction, and the world's weather.
Not (yet): rings and asteroid fields, orbits or spinning planets (planets stand still - a moving planet would move its ground under every creation), normal maps for ground textures, caves, grass, trees and rocks scattered on the ground, cloud shapes beyond the cover picture, the materials and look of sky, sea and terrain (they come from the game's template planet), a sea you can float on on more than one planet, and weather per planet.