class_name Chunk extends Node3D ## A single world chunk. Holds the chunk coordinate, bounds, and the ## visual/gameplay content for that region of the world. ## ## Chunks are the atomic unit of world geometry, gameplay objects, and ## persistence (see docs/architecture/world.md). The server loads and saves ## whole chunks; clients stream the chunks around them. ## Default chunk dimensions in world units (tiles). Matches the constants in ## docs/architecture/world.md section 2.2. const CHUNK_SIZE_X: int = 32 const CHUNK_SIZE_Y: int = 32 const CHUNK_SIZE_Z: int = 1 ## World-space origin of chunk coordinate (0,0). const CHUNK_ORIGIN := Vector3(0.0, 0.0, 0.0) ## Chunk coordinate (cx, cy, cz) in chunk space. var coord := Vector3i.ZERO ## The canonical chunk key: "{world_id}:{room_id}:{cx}:{cy}:{cz}". var key: String = "" ## World and room identifiers this chunk belongs to. var world_id: String = "" var room_id: String = "" ## Format version of the chunk payload (currently 1). var format_version: int = 1 ## Revision counter, incremented on every write. Used for optimistic ## concurrency and change detection. var revision: int = 0 ## Whether this chunk has been modified in memory but not yet flushed. var dirty: bool = false ## Container for static objects (props, geometry) in this chunk. var objects: Array[Node3D] = [] ## Container for dynamic object templates (spawnable entities). var dynamic_templates: Array[Node3D] = [] ## The world-space bounding box of this chunk. var bounds: AABB func _init(p_coord: Vector3i = Vector3i.ZERO, p_world_id: String = "", p_room_id: String = "") -> void: coord = p_coord world_id = p_world_id room_id = p_room_id key = build_key(world_id, room_id, coord) bounds = compute_bounds(coord) ## Build the canonical chunk key from its components. static func build_key(p_world_id: String, p_room_id: String, p_coord: Vector3i) -> String: return "%s:%s:%d:%d:%d" % [p_world_id, p_room_id, p_coord.x, p_coord.y, p_coord.z] ## Compute the world-space bounding box for a chunk coordinate. static func compute_bounds(p_coord: Vector3i) -> AABB: var min_pos := Vector3( CHUNK_ORIGIN.x + p_coord.x * CHUNK_SIZE_X, CHUNK_ORIGIN.y + p_coord.y * CHUNK_SIZE_Y, CHUNK_ORIGIN.z + p_coord.z * CHUNK_SIZE_Z ) return AABB(min_pos, Vector3(CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z)) ## Compute the chunk coordinate that contains a world-space position. static func coord_for_position(p_pos: Vector3) -> Vector3i: return Vector3i( int(floor((p_pos.x - CHUNK_ORIGIN.x) / CHUNK_SIZE_X)), int(floor((p_pos.y - CHUNK_ORIGIN.y) / CHUNK_SIZE_Y)), int(floor((p_pos.z - CHUNK_ORIGIN.z) / CHUNK_SIZE_Z)) ) ## Add a static object to this chunk. func add_object(p_object: Node3D) -> void: objects.append(p_object) add_child(p_object) ## Add a dynamic object template to this chunk. func add_dynamic_template(p_template: Node3D) -> void: dynamic_templates.append(p_template) add_child(p_template) ## Remove all objects from this chunk. func clear_objects() -> void: for obj in objects: if is_instance_valid(obj): obj.queue_free() objects.clear() dynamic_templates.clear() ## Mark this chunk as dirty (modified but not yet persisted). func mark_dirty() -> void: dirty = true revision += 1