You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.4 KiB
111 lines
3.4 KiB
class_name World |
|
extends Node |
|
|
|
const WORLDS_DIR := "user://worlds/" |
|
const FILE_EXTENSION := ".yf5" |
|
const MAGIC_NUMBER := 0x59463573 # "YF5s" |
|
const VERSION_NUMBER := 1 |
|
|
|
static var chunks_buffer := StreamBuffer.with_capacity(1024 * 1024) |
|
|
|
var last_saved := -1 # unix timestamp |
|
var playtime := 0.0 # in seconds |
|
var world_seed := randi() |
|
|
|
var chunks := Node.new() |
|
var generator := GeneratorSimple.new() |
|
|
|
func _init() -> void: |
|
chunks.name = "Chunks" |
|
add_child(chunks) |
|
|
|
func _process(delta: float) -> void: |
|
playtime += delta # Does not increase when paused. |
|
|
|
|
|
func get_block(pos: Vector2i) -> Block: |
|
return Block.new(self, pos) |
|
|
|
func get_chunk_or_null(pos: Vector2i) -> Chunk: |
|
var chunk_name := "Chunk %s" % pos |
|
var chunk := chunks.get_node_or_null(chunk_name) |
|
return chunk |
|
|
|
func get_or_create_chunk(pos: Vector2i) -> Chunk: |
|
var chunk_name := "Chunk %s" % pos |
|
var chunk := chunks.get_node_or_null(chunk_name) |
|
if chunk: return chunk |
|
|
|
chunk = Chunk.new(pos) |
|
chunk.name = chunk_name |
|
chunks.add_child(chunk) |
|
generator.generate(self, chunk) |
|
return chunk |
|
|
|
|
|
static func load(path: String) -> World: |
|
var world := World.new() |
|
|
|
var data := FileAccess.get_file_as_bytes(path) |
|
if data.is_empty(): push_error("Could not read file '%s': %s" |
|
% [ path, error_string(FileAccess.get_open_error()) ]); return null |
|
|
|
var file_buffer := StreamBuffer.from_bytes(data) |
|
var magic := file_buffer.read_uint32() |
|
var version := file_buffer.read_uint32() |
|
if magic != MAGIC_NUMBER : push_error("Magic number mismatch"); return null |
|
if version != VERSION_NUMBER: push_error("Version number mismatch"); return null |
|
|
|
chunks_buffer.write_raw_buffer(file_buffer.read_compressed(FileAccess.COMPRESSION_ZSTD)) |
|
var chunk_count := chunks_buffer.read_uint16() |
|
for i in chunk_count: |
|
var chunk := Chunk.load(chunks_buffer) |
|
world.chunks.add_child(chunk) |
|
chunks_buffer.clear() |
|
|
|
world.last_saved = FileAccess.get_modified_time(path) |
|
return world |
|
|
|
# TODO: Return whether successful. |
|
func save(path: String) -> void: |
|
var file := FileAccess.open(path + ".tmp", FileAccess.WRITE) |
|
if not file: push_error("Could not write to file '%s.tmp': %s" |
|
% [ path, error_string(FileAccess.get_open_error()) ]); return |
|
|
|
chunks_buffer.write_uint16(chunks.get_child_count()) |
|
for chunk: Chunk in chunks.get_children(): |
|
chunk.save(chunks_buffer) |
|
|
|
var file_buffer := StreamBuffer.with_capacity(1024 * 1024) |
|
file_buffer.write_uint32(MAGIC_NUMBER) |
|
file_buffer.write_uint32(VERSION_NUMBER) |
|
file_buffer.write_compressed(chunks_buffer.slice(), FileAccess.COMPRESSION_ZSTD) |
|
chunks_buffer.clear() |
|
|
|
if !file.store_buffer(file_buffer.slice()): |
|
push_error("Could not write to file '%s.tmp'" % path); return |
|
if DirAccess.rename_absolute(path + ".tmp", path): |
|
push_error("Could not rename .tmp to '%s'" % path); return |
|
|
|
last_saved = floori(Time.get_unix_time_from_system()) |
|
|
|
|
|
func spawn_chunk(chunk: Chunk, peer_id: int) -> void: |
|
chunk.save(chunks_buffer) |
|
_spawn_chunk.rpc_id(peer_id, chunks_buffer.slice()) |
|
chunks_buffer.clear() |
|
|
|
func despawn_chunk(chunk: Chunk, peer_id: int) -> void: |
|
_despawn_chunk.rpc_id(peer_id, chunk.chunk_pos) |
|
|
|
@rpc("reliable") |
|
func _spawn_chunk(bytes: PackedByteArray) -> void: |
|
var chunk := Chunk.load(StreamBuffer.from_bytes(bytes)) |
|
chunks.add_child(chunk) |
|
|
|
@rpc("reliable") |
|
func _despawn_chunk(chunk_pos: Vector2i) -> void: |
|
var chunk := get_chunk_or_null(chunk_pos) |
|
if not chunk: return |
|
chunks.remove_child(chunk) |
|
chunk.queue_free()
|
|
|