feat: stream chunks in-game, render 3D catalog props, and ground chunk coordinates
checkpoint / checkpoint (push) Successful in 5m54s

This commit is contained in:
2026-09-16 21:31:33 +00:00
parent e54ff6eeb2
commit d2b62c5d25
5 changed files with 412 additions and 56 deletions
+92 -13
View File
@@ -37,12 +37,65 @@ var _status_label: Label = null
var _build_editor: InGameEditor = null
var _stream_manager: ChunkStreamManager = null
func _ready() -> void:
_show_build_stamp()
if Session.editing:
_open_editor_for_world()
elif Session.active:
_setup_networked()
else:
if Session.active:
_setup_networked()
_setup_chunk_streaming()
func _setup_chunk_streaming() -> void:
if Session.world_id == null or int(Session.world_id) <= 0:
return
if _stream_manager != null and is_instance_valid(_stream_manager):
return
_stream_manager = ChunkStreamManager.new()
_stream_manager.name = "ChunkStreamManager"
_stream_manager.player = _player
_stream_manager.streaming_radius = 2
add_child(_stream_manager)
var chunk_source := HttpChunkSource.new()
chunk_source.name = "HttpChunkSource"
chunk_source.world_id = str(Session.world_id)
chunk_source.room_slug = "main"
chunk_source.stream_manager = _stream_manager
_stream_manager.add_child(chunk_source)
_stream_manager.set_server_rpc(chunk_source)
_stream_manager.is_active = true
_stream_manager.chunk_loaded.connect(_on_chunk_loaded)
_stream_manager._load_chunk(Vector3i.ZERO)
func _on_chunk_loaded(_coord: Vector3i, chunk_scene: Node) -> void:
if chunk_scene is ChunkScene:
var cs := chunk_scene as ChunkScene
if not cs.world_objects.is_empty() or not cs.tile_layers.is_empty():
_hide_default_test_props()
func _hide_default_test_props() -> void:
var props := get_node_or_null("Level/Props")
if props != null and props is Node3D:
(props as Node3D).visible = false
for child in props.get_children():
if child is CollisionObject3D:
(child as CollisionObject3D).set_collision_layer_value(1, false)
(child as CollisionObject3D).set_collision_mask_value(1, false)
var chest := get_node_or_null("Level/ScriptedChest")
if chest != null and chest is Node3D:
(chest as Node3D).visible = false
if chest is CollisionObject3D:
(chest as CollisionObject3D).set_collision_layer_value(1, false)
(chest as CollisionObject3D).set_collision_mask_value(1, false)
func _unhandled_input(event: InputEvent) -> void:
@@ -89,7 +142,29 @@ func _open_build_mode() -> void:
var room_slug := "main"
var coord := Vector3i.ZERO
var model := ChunkEditorModel.create_empty(32, 32, world_id_str, room_slug, coord)
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_player.set_physics_process(false)
_player.set_process_unhandled_input(false)
var save_service: ChunkSaveService = null
var model: ChunkEditorModel = null
var base_revision := 0
if int(world_id_str) > 0:
save_service = ChunkSaveService.new()
save_service.world_id = world_id_str
save_service.room_slug = room_slug
add_child(save_service)
var fetch_result: Dictionary = await save_service.fetch_chunk(coord)
if fetch_result.get("ok", false) and fetch_result.get("model") != null:
model = fetch_result["model"]
base_revision = int(fetch_result.get("revision", 0))
if model == null:
model = ChunkEditorModel.create_empty(32, 32, world_id_str, room_slug, coord)
base_revision = 0
var catalog := EditorObjectCatalog.new()
var err := catalog.load_catalog()
if err != OK:
@@ -97,20 +172,12 @@ func _open_build_mode() -> void:
var core := EditorCore.new(model, catalog)
_build_editor = InGameEditor.new(core)
if int(world_id_str) > 0:
var save_service := ChunkSaveService.new()
save_service.world_id = world_id_str
save_service.room_slug = room_slug
add_child(save_service)
_build_editor.setup_persistence(save_service, world_id_str, room_slug, coord, 0)
if save_service != null:
_build_editor.setup_persistence(save_service, world_id_str, room_slug, coord, base_revision)
_build_editor.closed.connect(_close_build_mode)
add_child(_build_editor)
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_player.set_physics_process(false)
_player.set_process_unhandled_input(false)
## Shared teardown for both close paths (F2 again, or the editor's own
## Close button/`closed` signal) -- safe to call twice: `queue_free()` on an
@@ -127,6 +194,14 @@ func _close_build_mode() -> void:
_player.set_process_unhandled_input(true)
_player._refresh_mouse_mode()
if _stream_manager == null:
_setup_chunk_streaming()
elif is_instance_valid(_stream_manager):
var source := _stream_manager.get_node_or_null("HttpChunkSource") as HttpChunkSource
if source != null and Session.world_id != null and int(Session.world_id) > 0:
source.world_id = str(Session.world_id)
_stream_manager.force_reload_chunk(Vector3i.ZERO)
## "Edit world" entry point from the world browser
## (world_persistence_impl.md §6 D4 / §9 "Client/editor task"). Reached only
@@ -198,6 +273,10 @@ func _leave_world() -> void:
## Close the ENet connection and stop it from calling us back mid-teardown.
func _teardown_net() -> void:
if _stream_manager != null and is_instance_valid(_stream_manager):
_stream_manager.clear_all_chunks()
_stream_manager.queue_free()
_stream_manager = null
if _net == null or not is_instance_valid(_net):
return
if _net.server_disconnected.is_connected(_on_server_lost):