refactor(M03): extract M2 prototype cache state

This commit is contained in:
2026-07-17 16:29:11 +04:00
parent 87bf067493
commit cd664eb4cb
19 changed files with 715 additions and 89 deletions
+157
View File
@@ -0,0 +1,157 @@
class_name M2PrototypeCacheState
extends RefCounted
## Owns detached static/animated M2 prototypes and their negative lookup
## outcomes until final renderer shutdown.
var _static_prototypes_by_path: Dictionary = {}
var _animated_prototypes_by_path: Dictionary = {}
var _missing_model_paths: Dictionary = {}
var _static_animation_paths: Dictionary = {}
## Returns whether a valid static prototype is retained for the normalized path.
func has_static_prototype(normalized_relative_path: String) -> bool:
return find_static_prototype(normalized_relative_path) != null
## Returns the exact retained static prototype or null.
func find_static_prototype(normalized_relative_path: String) -> Node3D:
return _find_valid_prototype(_static_prototypes_by_path, normalized_relative_path)
## Adopts a static prototype and returns the canonical retained reference. The
## first valid prototype wins; a later detached candidate is released.
func adopt_static_prototype(
normalized_relative_path: String,
prototype: Node3D) -> Node3D:
return _adopt_prototype(
_static_prototypes_by_path,
normalized_relative_path,
prototype
)
## Returns whether a valid animated prototype is retained for the normalized path.
func has_animated_prototype(normalized_relative_path: String) -> bool:
return find_animated_prototype(normalized_relative_path) != null
## Returns the exact retained animated prototype or null.
func find_animated_prototype(normalized_relative_path: String) -> Node3D:
return _find_valid_prototype(_animated_prototypes_by_path, normalized_relative_path)
## Adopts an animated prototype and returns the canonical retained reference.
func adopt_animated_prototype(
normalized_relative_path: String,
prototype: Node3D) -> Node3D:
return _adopt_prototype(
_animated_prototypes_by_path,
normalized_relative_path,
prototype
)
## Records that no model prototype can currently be loaded for this path.
func mark_model_missing(normalized_relative_path: String) -> bool:
return _mark_path(_missing_model_paths, normalized_relative_path)
## Returns whether the model path has a retained missing outcome.
func is_model_missing(normalized_relative_path: String) -> bool:
return not normalized_relative_path.is_empty() and _missing_model_paths.has(
normalized_relative_path
)
## Records that the path must use static presentation instead of animation.
func mark_animation_static(normalized_relative_path: String) -> bool:
return _mark_path(_static_animation_paths, normalized_relative_path)
## Returns whether the path has a retained static-animation outcome.
func is_animation_static(normalized_relative_path: String) -> bool:
return not normalized_relative_path.is_empty() and _static_animation_paths.has(
normalized_relative_path
)
## Returns detached path-only cache state for diagnostics and verification.
func diagnostic_snapshot() -> Dictionary:
return {
"static_prototype_paths": _sorted_paths(_static_prototypes_by_path),
"animated_prototype_paths": _sorted_paths(_animated_prototypes_by_path),
"missing_model_paths": _sorted_paths(_missing_model_paths),
"static_animation_paths": _sorted_paths(_static_animation_paths),
}
## Releases every retained prototype and clears positive and negative state.
## Detached Nodes are freed synchronously; in-tree Nodes are queued for deletion.
func clear_and_release() -> void:
_release_prototypes(_static_prototypes_by_path)
_release_prototypes(_animated_prototypes_by_path)
_missing_model_paths.clear()
_static_animation_paths.clear()
func _find_valid_prototype(
prototypes_by_path: Dictionary,
normalized_relative_path: String) -> Node3D:
if normalized_relative_path.is_empty():
return null
var prototype_variant: Variant = prototypes_by_path.get(normalized_relative_path)
if not (prototype_variant is Node3D):
return null
var prototype := prototype_variant as Node3D
return prototype if is_instance_valid(prototype) else null
func _adopt_prototype(
prototypes_by_path: Dictionary,
normalized_relative_path: String,
prototype: Node3D) -> Node3D:
if normalized_relative_path.is_empty() or prototype == null or not is_instance_valid(prototype):
return null
var retained_prototype := _find_valid_prototype(
prototypes_by_path,
normalized_relative_path
)
if retained_prototype != null:
if not is_same(retained_prototype, prototype):
_release_prototype(prototype)
return retained_prototype
prototypes_by_path[normalized_relative_path] = prototype
return prototype
func _mark_path(paths: Dictionary, normalized_relative_path: String) -> bool:
if normalized_relative_path.is_empty():
return false
paths[normalized_relative_path] = true
return true
func _release_prototypes(prototypes_by_path: Dictionary) -> void:
for prototype_variant in prototypes_by_path.values():
if prototype_variant is Node3D:
_release_prototype(prototype_variant as Node3D)
prototypes_by_path.clear()
func _release_prototype(prototype: Node3D) -> void:
if prototype == null or not is_instance_valid(prototype):
return
if prototype.is_inside_tree():
prototype.queue_free()
else:
prototype.free()
func _sorted_paths(paths: Dictionary) -> Array[String]:
var result: Array[String] = []
for path_variant in paths.keys():
result.append(String(path_variant))
result.sort()
return result
@@ -0,0 +1 @@
uid://srnqsus8pddp