Skip to content

server.world.db

Constants #

const container_slot_count = 27

container_slot_count is a chest's fixed slot count.

fn create_world_store #

fn create_world_store(worlds_dir string, name string, dim world.Dimension, generator string) !&WorldStore

create_world_store creates a fresh, empty world on disk under worlds_dir and returns its opened store. Errors if a world by that name already exists.

fn delete_world_files #

fn delete_world_files(worlds_dir string, name string) !

delete_world_files removes the on-disk folder for the named world. The caller is responsible for closing the LevelDB handle first - this only touches the filesystem. Refuses to delete anything outside worlds_dir or a world that isn't actually there.

fn discover_worlds #

fn discover_worlds(worlds_dir string) []string

discover_worlds returns the names of every subdirectory under worlds_dir that looks like a world (has a db folder).

fn load_named #

fn load_named(worlds_dir string, name string, generator_name string, dim world.Dimension) !&World

load_named opens the world stored under worlds_dir/name and pulls its overrides into memory.

fn new_stored_generator #

fn new_stored_generator(store Provider, fallback world.Generator) StoredGenerator

fn new_world #

fn new_world(name string, store ?Provider, generator_name string, dim world.Dimension) &World

fn open_leveldb #

fn open_leveldb(path string) !&LevelDB

fn open_world #

fn open_world(path string, dim world.Dimension) !&WorldStore

fn world_exists #

fn world_exists(worlds_dir string, name string) bool

world_exists reports whether a world folder with a db subdirectory is present under worlds_dir.

interface Factory #

interface Factory {
	exists(name string) bool
	discover() []string
mut:
	create(name string, dim world.Dimension, generator string) !Provider
	// open loads a persisted world, resolving its own generator/dimension
	// from whatever metadata the backend keeps (falling back to
	// fallback_generator/fallback_dim if it can't). Returns a fully loaded
	// World, not just a raw Provider, so a caller never has to reread
	// overrides a second time.
	open(name string, fallback_generator string, fallback_dim world.Dimension) !&World
	delete(name string) !
}

Factory creates, opens, lists and deletes named world backends. Hub only ever talks to worlds through this interface (plus the Provider/World it hands back). Swap it for something other than LevelDBFactory to back worlds with an entirely different storage mechanism.

interface Provider #

interface Provider {
	dimension() world.Dimension
	load_chunk(cx int, cz int) ?world.Chunk
	each_block(cb fn (x int, y int, z int, runtime_id int))
	each_tile(cb fn (x int, y int, z int, text string))
	each_container(cb fn (x int, y int, z int, items []ContainerSlotItem))
mut:
	set_block(x int, y int, z int, runtime_id int) !
	set_tile_text(x int, y int, z int, text string) !
	set_container_items(x int, y int, z int, items []ContainerSlotItem) !
	flush() !
	close() !
}

Provider is the storage backend contract a world needs, the same shape WorldStore (LevelDB) already implements, extracted so a framework user can bring their own backend instead of being stuck with LevelDB.

struct BlockOverride #

struct BlockOverride {
pub:
	x  int
	y  int
	z  int
	id int
}

struct ContainerSlotItem #

struct ContainerSlotItem {
pub mut:
	slot             int
	id               int
	meta             int
	count            int
	block_runtime_id int
	raw_extra_data   []u8
}

struct LevelDB #

@[heap]
struct LevelDB {
mut:
	db &leveldb.DB
}

fn (LevelDB) put #

fn (l &LevelDB) put(key []u8, value []u8) !

put returns write failures to the caller instead of discarding them, allowing persistence code to distinguish rejected writes from success.

fn (LevelDB) get #

fn (l &LevelDB) get(key []u8) ?[]u8

fn (LevelDB) delete #

fn (l &LevelDB) delete(key []u8) !

fn (LevelDB) each #

fn (l &LevelDB) each(cb fn (key []u8, value []u8))

fn (LevelDB) flush #

fn (l &LevelDB) flush() !

flush forces pending writes down to disk without releasing the handle, so a crash after a flush cannot lose the flushed data. close() already syncs, so this is only needed for periodic mid-run durability.

fn (LevelDB) close #

fn (l &LevelDB) close() !

struct LevelDBFactory #

struct LevelDBFactory {
pub:
	worlds_dir string
}

LevelDBFactory is Vedrock's own default, one LevelDB-backed folder per world under worlds_dir. It is a thin Factory shaped face over the existing manage.v/world_loader.v functions, not a reimplementation of them.

fn (LevelDBFactory) exists #

fn (f LevelDBFactory) exists(name string) bool

fn (LevelDBFactory) create #

fn (f LevelDBFactory) create(name string, dim world.Dimension, generator string) !Provider

fn (LevelDBFactory) open #

fn (f LevelDBFactory) open(name string, fallback_generator string, fallback_dim world.Dimension) !&World

fn (LevelDBFactory) discover #

fn (f LevelDBFactory) discover() []string

fn (LevelDBFactory) delete #

fn (f LevelDBFactory) delete(name string) !

struct ScheduledEntry #

struct ScheduledEntry {
pub:
	x   int
	y   int
	z   int
	due i64
}

ScheduledEntry represents one pending scheduled tick for a block position. It becomes due when current_tick reaches due.

Multiple entries may be queued for the same position; scheduled ticks are not deduplicated.

struct StoredGenerator #

struct StoredGenerator {
	store    Provider
	fallback world.Generator
	cache    &ChunkCache
}

fn (StoredGenerator) spawn_y #

fn (g StoredGenerator) spawn_y() int

fn (StoredGenerator) uses_blocks #

fn (g StoredGenerator) uses_blocks() bool

fn (StoredGenerator) generate #

fn (g StoredGenerator) generate(chunk_x int, chunk_z int) world.Chunk

fn (StoredGenerator) block_at #

fn (g StoredGenerator) block_at(x int, y int, z int) int

fn (StoredGenerator) biome_at #

fn (g StoredGenerator) biome_at(x int, z int) int

struct TickPosition #

struct TickPosition {
pub:
	x int
	y int
	z int
}

struct TileData #

struct TileData {
pub mut:
	text string
}

TileData is a block-entity's persistent data at a position.

struct TileEntry #

struct TileEntry {
pub:
	x    int
	y    int
	z    int
	text string
}

TileEntry is a TileData paired with its position, returned by tile_entries_in_chunk for chunk-send enrichment.

struct World #

@[heap]
struct World {
pub:
	name      string
	dimension world.Dimension = world.overworld
mut:
	store              ?Provider
	overrides          map[string]int
	tile_data          map[string]TileData
	container_data     map[string][]ContainerSlotItem
	open_holders       map[string]u64
	mutex              &sync.Mutex = sync.new_mutex()
	current_tick       i64
	scheduled          []ScheduledEntry
	last_persist_error ?string
	// Persistence worker state, only meaningful when store_backed is true.
	// A storeless World (tests, void worlds) never starts this thread and
	// must never touch these fields.
	store_backed    bool
	persist_mutex   &sync.Mutex = sync.new_mutex()
	persist_records []QueuedPersistRecord
	// persist_head points to the first pending persistence record.
	// Records before it have already been applied and are compacted
	// periodically, avoiding repeated front deletions from the queue.
	persist_head   int
	persist_wakeup chan bool = chan bool{cap: 1}
	persist_stop   chan bool = chan bool{cap: 1}
	persist_done   chan bool = chan bool{cap: 1}

	// Monotonic persistence totals used to measure enqueue and commit rates.
	persist_enqueued_count  &stdatomic.AtomicVal[i64] = stdatomic.new_atomic[i64](0)
	persist_committed_count &stdatomic.AtomicVal[i64] = stdatomic.new_atomic[i64](0)

	// Provider write latency and error state metrics.
	// consecutive_errors resets after the next successful write.
	persist_last_write_ns      &stdatomic.AtomicVal[i64] = stdatomic.new_atomic[i64](0)
	persist_longest_write_ns   &stdatomic.AtomicVal[i64] = stdatomic.new_atomic[i64](0)
	persist_consecutive_errors &stdatomic.AtomicVal[i64] = stdatomic.new_atomic[i64](0)

	// Persistence backlog thresholds. Tests may override these values.
	persist_high_water_threshold   int = persist_high_water_count
	persist_hard_ceiling_threshold int = persist_hard_ceiling_count

	// Timeout used while waiting for persistence shutdown to complete.
	persist_shutdown_timeout_value time.Duration = persist_shutdown_timeout

	// closing and closed make close() idempotent. closing prevents duplicate
	// stop signals while shutdown is still in progress; closed is set only
	// after the underlying store closes successfully.
	closing bool
	closed  bool
pub mut:
	generator_name string
}

World is a single loaded world, its persistent store plus the in memory cache of block overrides layered on top of the generated/vanilla chunks.

World writes become visible in memory immediately and reach disk asynchronously. flush and close wait until all previously queued writes have been persisted.

fn (World) block_count #

fn (w &World) block_count() int

fn (World) block_id #

fn (w &World) block_id(x int, y int, z int) int

block_id returns the block at the given position. It first checks for an in memory override, then falls back to the world's configured generator.

This matches the override first lookup used by session.block_at().

fn (World) block_override #

fn (w &World) block_override(x int, y int, z int) ?int

fn (World) clear_container #

fn (mut w World) clear_container(x int, y int, z int)

clear_container empties a container's contents (e.g. after its block is broken and its items have already been dropped into the world).

fn (World) close #

fn (mut w World) close() !

close waits for all queued persistence work to finish, stops the storage worker and then closes the underlying store.

The operation is idempotent. If a previous close attempt timed out, a retry waits for the existing shutdown to complete instead of sending a second stop signal.

fn (World) container_items #

fn (w &World) container_items(x int, y int, z int) []ContainerSlotItem

fn (World) container_slots #

fn (w &World) container_slots(x int, y int, z int) []types.ItemStack

container_slots resolves a container's contents into slot-indexed ItemStacks, empty ItemStack{} for any slot with nothing stored.

fn (World) due_scheduled_entries #

fn (mut w World) due_scheduled_entries(current i64, max int) []ScheduledEntry

due_scheduled_entries removes and returns up to max entries due at or before current. Additional due entries remain queued, bounding catchup work after stalls and preventing one tick from processing an unbounded backlog.

fn (World) flush #

fn (mut w World) flush() !

flush persists this world's store to disk without unloading it, waiting for every write already handed to the storage worker before this call to actually be applied first. So this reflects everything mutated up to the moment it was called.. Safe to call while the world is live. It doesn't touch the in memory override cache.

fn (World) is_persistent #

fn (w &World) is_persistent() bool

is_persistent reports whether the world is backed by on disk storage. Ephemeral in memory worlds have nothing to load or save, so external persistence tied to the world should only run when this returns true.

fn (World) last_persist_error #

fn (w &World) last_persist_error() ?string

fn (World) last_persist_write_duration #

fn (w &World) last_persist_write_duration() time.Duration

last_persist_write_duration returns the duration of the most recent provider write. Barrier signals are not included.

fn (World) load #

fn (mut w World) load()

load pulls every persisted block override and tile data entry into the in memory cache.

fn (World) longest_persist_write_duration #

fn (w &World) longest_persist_write_duration() time.Duration

fn (World) make_generator #

fn (w &World) make_generator(fallback world.Generator) world.Generator

make_generator wraps the given fallback with a StoredGenerator when this world has a backing store, so saved chunks are served before the fallback.

fn (World) oldest_pending_persist_age #

fn (w &World) oldest_pending_persist_age() time.Duration

oldest_pending_persist_age returns how long the oldest pending persistence record has been waiting. It returns zero when no records are pending.

fn (World) override_positions #

fn (w &World) override_positions() []TickPosition

override_positions returns the positions of all currently overridden blocks. Callers can use the snapshot to perform their own random tick selection.

fn (World) overrides_in_chunk #

fn (w &World) overrides_in_chunk(cx int, cz int) []BlockOverride

fn (World) pending_persist_count #

fn (w &World) pending_persist_count() int

pending_persist_count returns the number of persistence records waiting for the storage worker. It is safe to call from any thread.

fn (World) persist_committed_total #

fn (w &World) persist_committed_total() i64

persist_committed_total returns the total number of persistence records applied since this world started including writes and barriers.

fn (World) persist_consecutive_errors #

fn (w &World) persist_consecutive_errors() i64

persist_consecutive_errors returns the number of consecutive provider write failures since the last successful write.

fn (World) persist_enqueued_total #

fn (w &World) persist_enqueued_total() i64

persist_enqueued_total returns the total number of persistence records enqueued since this world started including writes and barriers.

fn (World) persist_hard_ceiling_threshold_value #

fn (w &World) persist_hard_ceiling_threshold_value() int

fn (World) persist_high_water_threshold_value #

fn (w &World) persist_high_water_threshold_value() int

persist_high_water_threshold_value and persist_hard_ceiling_threshold_value expose the configured overload policy thresholds for metrics/reporting.

fn (World) persist_pressure_level #

fn (w &World) persist_pressure_level() int

persist_pressure_level classifies the current persistence backlog: 0 is normal, 1 is at or above the high-water mark and 2 is at or above the hard ceiling where new persistence enqueues are subject to backpressure.

fn (World) release_container_hold #

fn (mut w World) release_container_hold(x int, y int, z int, runtime_id u64)

fn (World) schedule_tick #

fn (mut w World) schedule_tick(x int, y int, z int, delay int)

schedule_tick queues one scheduled tick for the given position. ScheduledTicker.scheduled_tick callback runs after delay game ticks.

fn (World) scheduled_backlog_count #

fn (w &World) scheduled_backlog_count() int

scheduled_backlog_count reports how many scheduled tick entries are currently queued, for runtime metrics. Safe to call from any thread.

fn (World) set_block #

fn (mut w World) set_block(x int, y int, z int, runtime_id int)

set_block updates the in memory override immediately, under mutex, then hands the actual disk write to the storage worker rather than performing it here. Callers see the new value right away regardless of disk speed; see the World comment above for exactly what that trades away.

fn (World) set_container_items #

fn (mut w World) set_container_items(x int, y int, z int, items []ContainerSlotItem)

set_container_items updates a container's in memory contents immediately, under mutex, then hands the actual disk write to the storage worker.

fn (World) set_container_slot #

fn (mut w World) set_container_slot(x int, y int, z int, slot int, stack types.ItemStack)

fn (World) set_persist_shutdown_timeout #

fn (mut w World) set_persist_shutdown_timeout(d time.Duration)

fn (World) set_tile_text #

fn (mut w World) set_tile_text(x int, y int, z int, text string)

set_tile_text updates the in memory tile data immediately, under mutex, then hands the actual disk write to the storage worker. The same split set_block uses, and for the same reason.

fn (World) tick #

fn (mut w World) tick(registry &block.Registry) []BlockOverride

tick advances this world by one game tick: fires every scheduled entry whose delay has elapsed, then rolls the random tick chance (see block.random_tick_speed) for every currently overridden block position. Only overridden positions are considered.

Returns the positions changed by either pass. World has no session/network knowledge, so broadcasting these to connected players is the caller's responsibility.

fn (World) tile_entries_in_chunk #

fn (w &World) tile_entries_in_chunk(cx int, cz int) []TileEntry

fn (World) tile_text #

fn (w &World) tile_text(x int, y int, z int) ?string

fn (World) try_hold_container #

fn (mut w World) try_hold_container(x int, y int, z int, runtime_id u64) bool

try_hold_container claims a container position for one session at a time.

struct WorldStore #

@[heap]
struct WorldStore {
	db        &LevelDB
	overrides &LevelDB
	dimension world.Dimension = world.overworld
}

fn (WorldStore) close #

fn (w &WorldStore) close() !

fn (WorldStore) dimension #

fn (w &WorldStore) dimension() world.Dimension

fn (WorldStore) each_block #

fn (w &WorldStore) each_block(cb fn (x int, y int, z int, runtime_id int))

fn (WorldStore) each_container #

fn (w &WorldStore) each_container(cb fn (x int, y int, z int, items []ContainerSlotItem))

fn (WorldStore) each_tile #

fn (w &WorldStore) each_tile(cb fn (x int, y int, z int, text string))

fn (WorldStore) flush #

fn (w &WorldStore) flush() !

flush persists both backing databases without closing them. Both are always attempted even if the first fails, so one handle's failure never leaves the other silently unflushed; the first error encountered, if any, is what's returned.

fn (WorldStore) load_chunk #

fn (w &WorldStore) load_chunk(cx int, cz int) ?world.Chunk

fn (WorldStore) set_block #

fn (w &WorldStore) set_block(x int, y int, z int, runtime_id int) !

fn (WorldStore) set_container_items #

fn (w &WorldStore) set_container_items(x int, y int, z int, items []ContainerSlotItem) !

set_container_items persists a container's contents.

fn (WorldStore) set_tile_text #

fn (w &WorldStore) set_tile_text(x int, y int, z int, text string) !

set_tile_text persists a block-entity's tex at a position, sharing the overrides handle with a distinct key prefix rather than opening a third LevelDB handle for no isolation benefit.