2D multiplayer platformer using Godot Engine
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

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()