106 lines
3.2 KiB
GDScript
106 lines
3.2 KiB
GDScript
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
|