class_name ChunkLoader extends Node2D @export var load_distance := 3 @export var unload_distance := 4 signal start_tracking(chunk: Chunk) signal stop_tracking (chunk: Chunk) var tracked_chunks: Dictionary[Vector2i, Variant] # value is unused # TODO: This is a pretty naive implementation, calling this every # update regardless of whether or how far the players moved. func _process(delta: float) -> void: if not multiplayer.is_server(): return var loader_chunk_pos := Chunk.pos_from_vec(global_position) # All currently tracked chunks start out in this dictionary. # Ones outside of `unload_distance` are removed in the loop. var out_of_range: Dictionary[Vector2i, Variant] = tracked_chunks.duplicate() var chunk_range := range(-unload_distance, unload_distance + 1) for x in chunk_range: for y in chunk_range: var chunk_pos := loader_chunk_pos + Vector2i(x, y) out_of_range.erase(chunk_pos) if tracked_chunks.has(chunk_pos): continue # Check to see is chunk is in `load_distance`. var in_load_range := (absi(x) <= load_distance) and ((absi(y) <= load_distance)) if not in_load_range: continue # it's not var chunk := Game.WORLD.get_or_create_chunk(chunk_pos) tracked_chunks[chunk_pos] = null chunk.tracked_by[self] = null start_tracking.emit(chunk) # Untrack any chunks outside of `unload_distance`. for chunk_pos in out_of_range: _forget(chunk_pos, true) func _exit_tree() -> void: for chunk_pos in tracked_chunks: _forget(chunk_pos, false) tracked_chunks.clear() func _forget(chunk_pos: Vector2i, emit_signal: bool) -> void: var chunk := Game.WORLD.get_chunk_or_null(chunk_pos) tracked_chunks.erase(chunk_pos) chunk.tracked_by.erase(self) if emit_signal: stop_tracking.emit(chunk) # FIXME: This is currently the only way a chunk gets removed: # When a player tracks it and stops tracking it. However, # there's other ways a chunk may be created. Consider this! if chunk.tracked_by.is_empty(): chunk.get_parent().remove_child(chunk) chunk.queue_free()