extends Node ## ## FeatureRegistry - catalog of every tool/feature and its RAM budget. ## ## This is what makes "100+ features that never OOM" possible: features are ## *descriptions*, not live objects. A feature is only instantiated when the ## user actually opens it, and torn down when they close it. The registry maps ## each feature id -> {name, module, cost_mb, loaded}. ## ## Modules are GDScript files under res://scripts/modules/. Each module is a ## Node that implements `_activate(host)` and `_deactivate()`. ## signal feature_activated(id: String) signal feature_deactivated(id: String) signal feature_failed(id: String, reason: String) ## id -> { name, module_path, cost_mb, node, active } var _features: Dictionary = {} ## Currently instantiated feature nodes, keyed by id. var _live: Dictionary = {} ## Parent node that hosts active feature modules. var host: Node = null func _ready() -> void: register_all() ## --- Public API ------------------------------------------------------------- ## Instantiate and activate a feature. Safe to call twice (no-op if active). func activate(id: String) -> bool: if not _features.has(id): feature_failed.emit(id, "unknown feature") return false if _live.has(id): feature_activated.emit(id) return true var f: Dictionary = _features[id] # Reserve memory *before* instantiating so eviction can happen first. MemoryManager.register(id, f["module_path"], float(f["cost_mb"])) var script: Resource = MemoryManager.acquire(id) if script == null or host == null: feature_failed.emit(id, "load failed or no host") MemoryManager.release(id) return false var node: Node = (script as GDScript).new() node.name = "Feature_" + id host.add_child(node) if node.has_method("_activate"): node.call("_activate", host) _live[id] = node feature_activated.emit(id) return true ## Deactivate and destroy a feature, returning its RAM to the pool. func deactivate(id: String) -> void: if not _live.has(id): return var node: Node = _live[id] if node.has_method("_deactivate"): node.call("_deactivate") node.queue_free() _live.erase(id) MemoryManager.release(id) # If nothing else references the module, drop the loaded script now. MemoryManager.purge(id) feature_deactivated.emit(id) func is_active(id: String) -> bool: return _live.has(id) func list_features() -> Array: var ids := _features.keys() ids.sort() return ids func feature_count() -> int: return _features.size() func get_info(id: String) -> Dictionary: return _features.get(id, {}) ## --- Registration ----------------------------------------------------------- func _reg(id: String, name: String, module_path: String, cost_mb: float) -> void: _features[id] = { "name": name, "module_path": module_path, "cost_mb": cost_mb, "active": false, } ## The full feature catalog. Costs are conservative RAM estimates in MB and are ## the numbers MemoryManager uses for eviction decisions. func register_all() -> void: # --- Mesh generation (Features 1-20) --- _reg("mesh.cube", "Cube Primitive", "res://scripts/modules/mesh_gen.gd", 0.5) _reg("mesh.sphere", "UV Sphere", "res://scripts/modules/mesh_gen.gd", 1.0) _reg("mesh.cylinder","Cylinder", "res://scripts/modules/mesh_gen.gd", 0.5) _reg("mesh.plane", "Plane", "res://scripts/modules/mesh_gen.gd", 0.2) _reg("mesh.suzanne", "Monkey Head", "res://scripts/modules/mesh_gen.gd", 2.0) _reg("mesh.torus", "Torus", "res://scripts/modules/mesh_gen.gd", 1.5) _reg("mesh.ico", "Icosphere", "res://scripts/modules/mesh_gen.gd", 1.0) _reg("mesh.text", "Text Mesh", "res://scripts/modules/mesh_gen.gd", 1.0) # --- Sculpting (Features 21-40) --- _reg("sculpt.draw", "Draw Brush", "res://scripts/modules/sculpt.gd", 4.0) _reg("sculpt.grab", "Grab Brush", "res://scripts/modules/sculpt.gd", 4.0) _reg("sculpt.pinch", "Pinch Brush", "res://scripts/modules/sculpt.gd", 4.0) _reg("sculpt.smooth", "Smooth Brush", "res://scripts/modules/sculpt.gd", 4.0) _reg("sculpt.flatten", "Flatten Brush", "res://scripts/modules/sculpt.gd", 4.0) # --- Rigging (Features 41-60) --- _reg("rig.skeleton", "Armature Editor", "res://scripts/modules/rigging.gd", 8.0) _reg("rig.weight", "Weight Paint", "res://scripts/modules/rigging.gd", 6.0) # --- Simulation (Features 61-80) --- _reg("sim.cloth", "Cloth Sim", "res://scripts/modules/simulation.gd", 12.0) _reg("sim.hair", "Hair Sim", "res://scripts/modules/simulation.gd", 10.0) _reg("sim.wind", "Wind Field", "res://scripts/modules/simulation.gd", 2.0) # --- Animation (Features 81-100) --- _reg("anim.timeline", "Timeline", "res://scripts/modules/timeline.gd", 3.0) _reg("anim.keyframe", "Keyframe Tool","res://scripts/modules/timeline.gd", 3.0) print("[FeatureRegistry] %d features registered" % _features.size())