
Godot Project Foundations
- 243 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-project-foundations for development tasks
About
godot-project-foundations: A skill for development. This provides functionality for development workflows.
- godot-project-foundations
Godot Project Foundations by the numbers
- 243 all-time installs (skills.sh)
- +29 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,601 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-project-foundationsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 243 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-project-foundations for development tasks
Files
Project Foundations
Feature-based organization, consistent naming, and version control hygiene define professional Godot projects.
Available Scripts
🏛️ Core Scaffolding (Stateful / Persistent)
Stateful managers and controllers that persist in the SceneTree or handle global lifecycle events.
- [project_bootstrapper.gd](scripts/project_bootstrapper.gd): Auto-generates feature folders and .gitignore.
- [runtime_configurator.gd](scripts/runtime_configurator.gd): Applies high-performance profiles and saves
override.cfg. - [managed_autoload.gd](scripts/managed_autoload.gd): Advanced Singleton pattern with
RefCounteddelegation. - [global_event_bus.gd](scripts/global_event_bus.gd): Strongly-typed global Signal Bus for system decoupling.
- [node_pooling_system.gd](scripts/node_pooling_system.gd): Thread-safe Object Pool for high-frequency scene instantiation.
- [async_resource_loader.gd](scripts/async_resource_loader.gd): Threaded non-blocking scene loading with progress status.
🛠️ Runtime Utilities (Stateless / Lightweight)
Stateless helper scripts, static libraries, and custom data containers (Resource/RefCounted).
- [base_data_resource.gd](scripts/base_data_resource.gd): Reactive Resource foundation using
emit_changed(). - [advanced_telemetry_logger.gd](scripts/advanced_telemetry_logger.gd): Custom OS-level
Loggerfor crash reporting. - [threaded_task_worker.gd](scripts/threaded_task_worker.gd): Robust
WorkerThreadPoolimplementation. - [action_buffer_input.gd](scripts/action_buffer_input.gd): Foundational
_unhandled_inputbuffer. - [build_metadata_provider.gd](scripts/build_metadata_provider.gd): Native extraction of version and build metadata.
Do NOT Load dependency_auditor.gd unless troubleshooting loading errors.
NEVER Do (Expert Anti-Patterns)
Global Architecture
- NEVER group by file type —
/scripts,/spritesfolders. Nightmare maintainability. Use feature-based:/player,/ui. - NEVER mix snake_case and PascalCase in files — Standard: snake_case for files, PascalCase for nodes.
- NEVER use hardcoded get_node() paths — Brittle on reparenting. Use
%SceneUniqueNamesfor stable references. - NEVER use monolithic Autoloads — Avoid managers that hold visual node references; keep singletons focused on pure data or RefCounted delegation.
Resource Management
- NEVER forget .gitignore — Committing
.godot/folder = 100MB+ bloat + conflicts. - NEVER skip .gdignore for raw assets — Design source files (
.psd,.blend) in root will be imported unless ignored. - NEVER modify globally shared Resources directly — Strictly call
duplicate(true)for unique instances with independent state.
Performance & Threading
- NEVER block the main thread with `load()` — Strictly use
ResourceLoader.load_threaded_request()for async scene transitions. - NEVER modify the SceneTree from a background thread — Strictly use
call_deferred()for thread-to-main-thread synchronization. - NEVER skip Mutex locking during pooled access — Strictly ensure thread-safety when using a shared
WorkerThreadPoolor Object Pool. - NEVER use `_process()` for precise input — Tied to visual framerate. Strictly use
_unhandled_input()to capture exact, frame-independent events.
---
1. Naming Conventions
- Files & Folders: Always use
snake_case. (e.g.,player_controller.gd,main_menu.tscn). - Exception: C# scripts use
PascalCasefor class-match. - Node Names: Always use
PascalCase(e.g.,PlayerSprite,CollisionShape2D). - Exported Variables: Use
snake_case. (e.g.,@export var max_health: int). The Inspector automatically converts this to Title Case ("Max Health"). - Internal / Private Members: Prepend a single underscore
_to variables and methods that are internal to the class. (e.g.,var _current_health,func _calculate_damage()). This also applies to virtual engine callbacks (e.g.,_ready,_process). - Signals: Use past-tense
snake_caseto represent events that have already occurred. (e.g.,signal health_changed,signal door_opened). - Unique Names: Use
%SceneUniqueNamesfor frequently accessed nodes to avoid brittleget_node()paths.
2. Feature-Based Organization
Instead of grouping by type (e.g., /scripts, /sprites), group by feature (the "What", not the "How").
Correct Structure:
/project.godot
/common/ # Global resources, themes, shared scripts
/entities/
/player/ # Everything related to player
player.tscn
player.gd
player_sprite.png
/enemy/
/ui/
/main_menu/
/levels/
/addons/ # Third-party plugins3. Version Control
- Always include a
.gitignoretailored for Godot (ignoring.godot/folder and import artifacts). - Use
.gdignorein folders that Godot should not scan/import (e.g., raw design source files).
Workflow: Scaffolding a New Project
When asked to "Setup a project" or "Start a new game":
1. Initialize Root: Ensure project.godot exists. 2. Create Core Folders:
entities/ui/levels/common/
3. Setup Git: Create a comprehensive .gitignore. 4. Documentation: Create a README.md explaining the feature-based structure.
🔄 Migration Guide: Typed GDScript 2.0
Transitioning from untyped GDScript or C# to strictly-typed GDScript 2.0.
1. The Inference Operator (:=)
Use := when the type is obvious from the right-hand side.
- Good:
var pos := Vector2(10, 10) - Redundant:
var pos: Vector2 = Vector2(10, 10)
2. Typed Collections
Godot 4 introduces statically typed arrays and dictionaries.
- Array:
var enemies: Array[Enemy] = [] - Dictionary:
var spawn_rates: Dictionary[StringName, float] = {}
3. Explicit Return Types
Always specify the return type, even if it is void.
func take_damage(amount: int) -> void:
4. Safe Casting (as)
Use the as keyword to guarantee a type and get "safe lines" (green line numbers in the editor).
var timer := $Timer as Timer
5. Enforce Strictness
In Project Settings > Debug > GDScript, set Untyped Declaration to Warn or Error.
Expert Foundation Architectures
1. Scene Transition Manager (Threaded)
Avoid blocking the main thread during scene changes by using ResourceLoader.load_threaded_request(). This allows you to show an animated loading screen while the background thread parses the next level.
class_name SceneManager extends Node
## Autoload: Manages threaded scene transitions.
signal progress_updated(percent: float)
var _target_path: String = ""
func load_scene(path: String) -> void:
_target_path = path
if ResourceLoader.load_threaded_request(path) == OK:
set_process(true)
func _process(_delta: float) -> void:
var progress := []
var status := ResourceLoader.load_threaded_get_status(_target_path, progress)
match status:
ResourceLoader.THREAD_LOAD_IN_PROGRESS:
progress_updated.emit(progress[0] * 100)
ResourceLoader.THREAD_LOAD_LOADED:
var scene := ResourceLoader.load_threaded_get(_target_path) as PackedScene
get_tree().change_scene_to_packed(scene)
set_process(false)2. Global Event Bus (Decoupled)
Maintain strict decoupling by using a global "Event Bus" Autoload. This allows distant systems (e.g., UI and Boss AI) to communicate without knowing each other's existence.
class_name EventBus extends Node
## Autoload: Central hub for global signals.
signal player_died
signal quest_completed(quest_id: String)
signal boss_phase_changed(new_phase: int)
# Usage (Publisher):
# EventBus.player_died.emit()
# Usage (Subscriber):
# EventBus.player_died.connect(_on_player_death)3. Project Metadata (Resource-Based)
Centralize project info like versioning, build dates, and feature flags in a dedicated Resource. This is type-safe and more performant than parsing raw JSON/TXT files.
class_name ProjectMetadata extends Resource
## Data container for project-wide configuration.
@export var version: String = "1.0.0"
@export var build_date: String = "2026-04-30"
@export var debug_mode: bool = false
func save_metadata(path: String = "user://metadata.res") -> void:
ResourceSaver.save(self, path)
static func load_metadata(path: String = "user://metadata.res") -> ProjectMetadata:
if ResourceLoader.exists(path):
return ResourceLoader.load(path) as ProjectMetadata
return ProjectMetadata.new()Reference
- Official Docs:
tutorials/best_practices/project_organization.rst - Official Docs:
tutorials/best_practices/scene_organization.rst
Related
- Master Skill: godot-master
# Godot-specific ignores
.godot/
*.import
# Mono/C# ignores
.mono/
*.sln
*.csproj
.vs/
# System ignores
.DS_Store
Thumbs.db
class_name ActionBufferInput
extends Node
## Foundational _unhandled_input buffer to decouple hardware events from frame-rate logic.
## Prevents UI click-through and captures raw events before SceneTree propagation.
@export var buffer_window_ms: int = 150
var _input_buffer: Dictionary = {}
func _unhandled_input(event: InputEvent) -> void:
# Capture specific actions into a timestamped buffer
if event.is_action_pressed(&"jump"):
_buffer_action(&"jump")
# Mark as handled to prevent propagation to UI or other layers
get_viewport().set_input_as_handled()
if event.is_action_pressed(&"attack"):
_buffer_action(&"attack")
get_viewport().set_input_as_handled()
func _buffer_action(action: StringName) -> void:
_input_buffer[action] = Time.get_ticks_msec()
func is_action_buffered(action: StringName) -> bool:
if _input_buffer.has(action):
var delta = Time.get_ticks_msec() - _input_buffer[action]
if delta <= buffer_window_ms:
return true
return false
func consume_action(action: StringName) -> void:
_input_buffer.erase(action)
## Advanced Telemetry Logger
## Intercepts engine print/error streams and routes them to a custom sink.
## Mandatory for production build crash reporting and remote diagnostics.
extends Node
# Implementation of a custom Logger requires inheritance and OS registration
class TelemetrySink extends Logger:
var _log_mutex := Mutex.new()
var _session_log: Array[String] = []
func _log_error(message: String) -> void:
_process_log(message, true)
func _log_message(message: String, _level: int = 0) -> void:
_process_log(message, false)
func _process_log(message: String, is_error: bool) -> void:
# Multi-threaded safety is CRITICAL for Loggers
_log_mutex.lock()
var entry = "[%s] %s: %s" % [
Time.get_time_string_from_system(),
"ERROR" if is_error else "LOG",
message
]
_session_log.append(entry)
# Expert: Route to remote telemetry server or secure local encrypted file here
# Do NOT use print() here; it causes infinite recursion.
_log_mutex.unlock()
func _enter_tree() -> void:
# Register the sink into the OS to capture ALL engine output
# This should be done as early as possible in the project lifecycle.
OS.add_logger(TelemetrySink.new())
print("Foundations: Telemetry System Online.")
class_name AsyncResourceLoader
extends Node
## Expert boilerplate for background loading with progress indicators.
## Utilizes ResourceLoader.load_threaded_request for non-blocking transitions.
signal loading_completed(resource: Resource)
signal loading_failed(path: String)
signal progress_updated(percent: float)
var _target_path: String = ""
func request_load(path: String) -> void:
if not ResourceLoader.exists(path):
loading_failed.emit(path)
return
_target_path = path
# Start background thread load
ResourceLoader.load_threaded_request(path, "", true)
set_process(true)
func _process(_delta: float) -> void:
if _target_path == "":
set_process(false)
return
var progress = []
var status = ResourceLoader.load_threaded_get_status(_target_path, progress)
match status:
ResourceLoader.THREAD_LOAD_IN_PROGRESS:
progress_updated.emit(progress[0] * 100.0)
ResourceLoader.THREAD_LOAD_LOADED:
var res = ResourceLoader.load_threaded_get(_target_path)
loading_completed.emit(res)
_target_path = ""
ResourceLoader.THREAD_LOAD_FAILED, ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
loading_failed.emit(_target_path)
_target_path = ""
@icon("res://icons/data_resource.svg")
class_name BaseDataResource
extends Resource
## Reactive Resource foundation for scalability.
## Centralizes data-driven triggers by emitting changed signals on property setters.
@export_group("Identity")
@export var id: StringName
@export var display_name: String = "New Item"
@export_group("Statistics")
@export var base_value: float = 1.0:
set(v):
base_value = v
emit_changed()
@export var metadata: Dictionary = {}:
set(v):
metadata = v
emit_changed()
## Deep duplicate helper to ensure nested resources are truly decoupled
func clone() -> BaseDataResource:
return self.duplicate(true)
## Expert Tip: Always emit_changed() when modifying custom lists or internal states
## to ensure UI components or other listeners can react to the Resource change.
func update_metadata(key: String, value: Variant) -> void:
metadata[key] = value
emit_changed()
class_name BuildMetadataProvider
extends RefCounted
## Native extraction of project version and build metadata.
## Useful for display in HUD, settings, or for log signatures.
static func get_version_string() -> String:
var version = ProjectSettings.get_setting("application/config/version", "0.0.1")
var build_tag = "DEBUG" if OS.is_debug_build() else "RELEASE"
# Attempt to get platform suffix
var platform = OS.get_name()
return "v%s-%s [%s]" % [version, build_tag, platform]
static func get_project_name() -> String:
return ProjectSettings.get_setting("application/config/name", "Godot Project")
static func is_production() -> bool:
return not OS.is_debug_build() and not Engine.is_editor_hint()
## Custom metadata can be injected during export via CLI --version-hash
static func get_custom_metadata(key: String, default: Variant = null) -> Variant:
return ProjectSettings.get_setting("metadata/" + key, default)
# skills/project-foundations/scripts/dependency_auditor.gd
@tool
extends EditorScript
## Dependency Auditor Expert Pattern
## Analyzes scene dependencies to detect circular references and coupling issues.
func _run() -> void:
print("=== Dependency Auditor ===")
var dependency_map := {}
# Build dependency graph
_build_dependency_map("res://", dependency_map)
# Detect circular dependencies
var circular := _detect_circular_deps(dependency_map)
if not circular.is_empty():
print("⚠️ Circular dependencies detected:")
for cycle in circular:
print(" %s" % cycle)
# Report highly coupled scenes
_report_coupling(dependency_map)
func _build_dependency_map(path: String, dep_map: Dictionary) -> void:
var dir := DirAccess.open(path)
if not dir:
return
dir.list_dir_begin()
var file_name := dir.get_next()
while file_name != "":
var full_path := path + file_name
if dir.current_is_dir():
if not file_name.begins_with("."):
_build_dependency_map(full_path + "/", dep_map)
elif file_name.ends_with(".tscn"):
dep_map[full_path] = _extract_dependencies(full_path)
file_name = dir.get_next()
func _extract_dependencies(scene_path: String) -> Array:
var deps := []
var file := FileAccess.open(scene_path, FileAccess.READ)
if not file:
return deps
var content := file.get_as_text()
# Parse ext_resource entries
var regex := RegEx.new()
regex.compile('ext_resource.*path="([^"]+)"')
for match in regex.search_all(content):
var resource_path: String = match.get_string(1)
if resource_path.ends_with(".tscn"):
deps.append(resource_path)
return deps
func _detect_circular_deps(dep_map: Dictionary) -> Array:
var circular := []
for scene in dep_map:
var visited := {}
var path := []
if _has_cycle(scene, dep_map, visited, path):
circular.append(" -> ".join(path))
return circular
func _has_cycle(scene: String, dep_map: Dictionary, visited: Dictionary, path: Array) -> bool:
if scene in path:
path.append(scene)
return true
if visited.get(scene, false):
return false
visited[scene] = true
path.append(scene)
for dep in dep_map.get(scene, []):
if _has_cycle(dep, dep_map, visited, path):
return true
path.pop_back()
return false
func _report_coupling(dep_map: Dictionary) -> void:
# Find scenes with high dependency counts
print("\n=== Coupling Analysis ===")
for scene in dep_map:
var dep_count := dep_map[scene].size()
if dep_count > 5:
print("⚠️ High coupling: %s (%d dependencies)" % [scene, dep_count])
## EXPERT NOTE:
## Circular dependencies = runtime loading errors in Godot.
## NEVER structure scenes as: Player -> Weapon -> Player
## FIX: Use Resources for shared data, signals for communication.
## Run this auditor monthly on projects >50 scenes.
# skills/project-foundations/scripts/feature_scaffolder.gd
@tool
extends EditorScript
## Feature Scaffolder Expert Pattern
## Generates complete feature folders with base scenes and scripts.
@export var feature_name: String = "new_feature"
@export var feature_type: String = "entity" # entity, ui, system
const TEMPLATES := {
"entity": {
"scene": "base_entity.tscn",
"script": "entity_template.gd",
"folders": ["states", "abilities"]
},
"ui": {
"scene": "base_ui.tscn",
"script": "ui_controller.gd",
"folders": ["components", "themes"]
},
"system": {
"script": "system_template.gd",
"folders": ["data", "processors"]
}
}
func _run() -> void:
if feature_name.is_empty():
printerr("❌ Feature name cannot be empty")
return
var feature_path := "res://%ss/%s/" % [feature_type, feature_name]
# Create feature folder
if DirAccess.dir_exists_absolute(feature_path):
printerr("❌ Feature '%s' already exists" % feature_name)
return
DirAccess.make_dir_recursive_absolute(feature_path)
print("✓ Created %s" % feature_path)
# Create template files
var template := TEMPLATES.get(feature_type, {})
if template.has("scene"):
_create_base_scene(feature_path + feature_name + ".tscn")
if template.has("script"):
_create_base_script(feature_path + feature_name + ".gd")
# Create subfolders
for subfolder in template.get("folders", []):
var sub_path := feature_path + subfolder
DirAccess.make_dir_recursive_absolute(sub_path)
print("✓ Created %s" % sub_path)
# Create README
_create_readme(feature_path)
print("✅ Feature '%s' scaffolded successfully!" % feature_name)
func _create_base_scene(path: String) -> void:
# Create minimal scene structure
var scene := PackedScene.new()
var root: Node
match feature_type:
"entity":
root = CharacterBody2D.new()
"ui":
root = Control.new()
_:
root = Node.new()
root.name = feature_name.capitalize().replace(" ", "")
scene.pack(root)
ResourceSaver.save(scene, path)
print("✓ Created scene: %s" % path)
func _create_base_script(path: String) -> void:
var script_content := """extends %s
## %s
## TODO: Add description
func _ready() -> void:
pass
## EXPERT NOTE:
## Follow single responsibility principle.
## Keep scripts under 300 lines - break into modules if larger.
""" % [_get_base_class(), feature_name.capitalize()]
var file := FileAccess.open(path, FileAccess.WRITE)
file.store_string(script_content)
print("✓ Created script: %s" % path)
func _get_base_class() -> String:
match feature_type:
"entity": return "CharacterBody2D"
"ui": return "Control"
_: return "Node"
func _create_readme(feature_path: String) -> void:
var readme := """# %s
## Purpose
TODO: Describe this feature
## Structure
- Main scene: `%s.tscn`
- Main script: `%s.gd`
## Dependencies
TODO: List related features/systems
## Usage
TODO: How to integrate this feature
""" % [feature_name.capitalize(), feature_name, feature_name]
var file := FileAccess.open(feature_path + "README.md", FileAccess.WRITE)
file.store_string(readme)
print("✓ Created README")
## EXPERT NOTE:
## Customize TEMPLATES dict for your project's patterns.
## Advanced: Generate test scenes automatically with GUT framework.
## Pro tip: Bind this to EditorPlugin tool button for one-click scaffolding.
## Global Event Bus (Autoload: EventBus)
## Strongly-typed Signal Bus for system decoupling.
## Allows nodes to communicate without direct dependencies.
extends Node
# Expert: Group signals by domain for clarity
# --- Player Signals ---
signal player_spawned(player: Node2D)
signal player_died(reason: StringName)
signal player_health_changed(current: int, max: int)
# --- World Signals ---
signal level_started(id: StringName)
signal level_completed(id: StringName)
# --- System Signals ---
signal save_requested
signal settings_updated(config: Dictionary)
## Tip: Use StringName (&"name") for signal parameters to avoid string overhead.
## Tip: Always include 'player' or 'sender' references if multiple instances exist.
## Managed Autoload Pattern
## Use this as a foundation for singletons that delegate logic to RefCounted classes.
## This prevents "Monolithic Singleton" bloat by keeping the Node skeleton light.
extends Node
# Use static variables for globally shared data without instance overhead checks
static var run_id: int = 0
static var session_start_time: float = 0.0
# Delegation: Systems are RefCounted classes, NOT nodes, to keep the Tree clean
var economy_system: RefCounted
var achievement_system: RefCounted
func _enter_tree() -> void:
# Autoloads should strictly manage business logic; avoid UI/Visual references.
process_mode = Node.PROCESS_MODE_ALWAYS
session_start_time = Time.get_unix_time_from_system()
_initialize_subsystems()
func _initialize_subsystems() -> void:
# Dependency Injection style initialization
# economy_system = EconomyEngine.new()
pass
static func get_uptime() -> float:
return Time.get_unix_time_from_system() - session_start_time
class_name NodePoolingSystem
extends Node
## Thread-safe, cross-platform Object Pool for high-frequency spawns (bullets, FX).
## Bypasses SceneTree overhead by reusing existing nodes.
@export var pool_size: int = 32
@export var template: PackedScene
var _pool: Array[Node] = []
var _mutex := Mutex.new()
func _ready() -> void:
for i in range(pool_size):
_spawn_to_pool()
func _spawn_to_pool() -> void:
var instance = template.instantiate()
instance.hide()
# Set process_mode to disabled while in pool to save cycles
instance.process_mode = Node.PROCESS_MODE_DISABLED
add_child(instance)
_pool.append(instance)
func get_node_from_pool() -> Node:
_mutex.lock()
for n in _pool:
if n.process_mode == Node.PROCESS_MODE_DISABLED:
n.process_mode = Node.PROCESS_MODE_INHERIT
n.show()
_mutex.unlock()
return n
# Dynamic expansion if pool is empty
_spawn_to_pool()
var last = _pool.back()
last.process_mode = Node.PROCESS_MODE_INHERIT
last.show()
_mutex.unlock()
return last
func return_to_pool(node: Node) -> void:
_mutex.lock()
node.hide()
node.process_mode = Node.PROCESS_MODE_DISABLED
_mutex.unlock()
# skills/project-foundations/code/project_bootstrapper.gd
@tool
extends EditorScript
## Project Foundations Expert Pattern
## Automatically scaffolds a professional feature-based structure.
const CORE_FOLDERS = [
"res://common",
"res://entities",
"res://levels",
"res://ui",
"res://assets",
"res://data"
]
func _run() -> void:
print("--- Starting Project Bootstrap ---")
# 1. Feature-Based Folder Creation
# Expert logic: Group objects by feature/entity rather than by
# file type (e.g. res://entities/player/player.tscn over res://scenes/player.tscn).
for folder in CORE_FOLDERS:
if not DirAccess.dir_exists_absolute(folder):
DirAccess.make_dir_recursive_absolute(folder)
print("[CREATED] ", folder)
# 2. Sub-folder Scaffolding (The Module Pattern)
_create_subfolder("res://entities/player")
_create_subfolder("res://common/globals")
# 3. .gdignore Enforcement
# Ensure specific folders are IGNORED by Godot's importer
# if they contain non-game assets (e.g. blend files).
_enforce_gdignore("res://assets/raw_source")
print("--- Bootstrap Complete ---")
func _create_subfolder(path: String) -> void:
if not DirAccess.dir_exists_absolute(path):
DirAccess.make_dir_recursive_absolute(path)
print("[CREATED] ", path)
func _enforce_gdignore(path: String) -> void:
if DirAccess.dir_exists_absolute(path):
var file = FileAccess.open(path + "/.gdignore", FileAccess.WRITE)
file.store_string("")
## EXPERT NOTE:
## Use 'Project Setup CLI': Tie this script to a custom EditorPlugin
## button to allow artists and designers to quickly 'Scaffold New Feature'
## with all 'base_scenes' and 'logic_scripts' pre-populated.
## NEVER put code files in the root folder. For 'project-foundations',
## enforce a strict 'res://common' layer for Autoloads and 'res://data'
## for JSON/Resource databases.
class_name RuntimeConfigurator
extends Node
## Expert pattern for applying high-performance profiles and saving override.cfg.
## This allows dynamic adjustments to ticks, FPS, and window modes while persisting them.
static func apply_high_performance_profile() -> void:
# High-tick physics for competitive/sports games
Engine.max_fps = 144
Engine.physics_ticks_per_second = 120
# Window settings must be routed through the DisplayServer for immediate effect
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)
# Setting persistence via override.cfg [Standard Godot startup override]
ProjectSettings.set_setting("application/run/max_fps", 144)
ProjectSettings.set_setting("physics/common/physics_ticks_per_second", 120)
# Only call this in tools or at specific "Apply" moments to avoid I/O blocking
ProjectSettings.save_custom("user://override.cfg")
static func apply_battery_saver_profile() -> void:
Engine.max_fps = 30
Engine.physics_ticks_per_second = 60
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
ProjectSettings.set_setting("application/run/max_fps", 30)
ProjectSettings.save_custom("user://override.cfg")
# skills/project-foundations/scripts/scene_naming_validator.gd
@tool
extends EditorScript
## Scene Naming Validator Expert Pattern
## Scans project for naming convention violations and %SceneUniqueName usage.
func _run() -> void:
print("=== Scene Naming Validator ===")
var violations := []
# Scan all .tscn files
_scan_directory("res://", violations)
if violations.is_empty():
print("✓ All scenes follow naming conventions!")
else:
print("❌ Found %d violations:" % violations.size())
for v in violations:
print(" - %s" % v)
func _scan_directory(path: String, violations: Array) -> void:
var dir := DirAccess.open(path)
if not dir:
return
dir.list_dir_begin()
var file_name := dir.get_next()
while file_name != "":
var full_path := path + file_name
if dir.current_is_dir():
if not file_name.begins_with("."):
_scan_directory(full_path + "/", violations)
elif file_name.ends_with(".tscn"):
_validate_scene(full_path, violations)
file_name = dir.get_next()
func _validate_scene(scene_path: String, violations: Array) -> void:
# 1. Check filename is snake_case
var filename := scene_path.get_file().get_basename()
if not _is_snake_case(filename):
violations.append("%s: Filename not snake_case" % scene_path)
# 2. Load scene and check node names
var scene := load(scene_path)
if scene:
var root := scene.instantiate()
_check_node_naming(root, scene_path, violations)
root.queue_free()
func _check_node_naming(node: Node, scene_path: String, violations: Array) -> void:
# Node names should be PascalCase
if not _is_pascal_case(node.name):
violations.append("%s: Node '%s' not PascalCase" % [scene_path, node.name])
for child in node.get_children():
_check_node_naming(child, scene_path, violations)
func _is_snake_case(text: String) -> bool:
# Valid snake_case: all lowercase, numbers, underscores
var regex := RegEx.new()
regex.compile("^[a-z0-9_]+$")
return regex.search(text) != null
func _is_pascal_case(text: String) -> bool:
# Valid PascalCase: starts with uppercase, alphanumeric
if text.is_empty() or not text[0].to_upper() == text[0]:
return false
# Built-in nodes like "Area2D" are always valid
return true
## EXPERT NOTE:
## Run this validator BEFORE submitting PRs or releasing builds.
## CRITICAL: %SceneUniqueNames are NOT validated here - use runtime checks.
## For CI/CD: export violations to JSON for automated PR blocking.
class_name ThreadedTaskWorker
extends Node
## Expert implementation of WorkerThreadPool for heavy calculations.
## Ensures thread-safe global state updates using Mutex and SceneTree synchronization.
var _shared_results: Array = []
var _data_mutex := Mutex.new()
func execute_async_calculation(data: Variant) -> void:
# Dispatch to the engine's built-in thread pool [Efficient multi-core usage]
WorkerThreadPool.add_task(_background_processing.bind(data), true, "Heavy AI/ProcGen Task")
func _background_processing(input_data: Variant) -> void:
var local_buffer := []
# ... Perform heavy heavy work here ...
# local_buffer.append(processed_result)
# Only lock when committing to the shared state to minimize core contention
_data_mutex.lock()
_shared_results.append_array(local_buffer)
_data_mutex.unlock()
# ALWAYS use call_deferred to update nodes or emit signals to the UI
call_deferred(&"_on_task_finalized")
func _on_task_finalized() -> void:
# Back on main thread: Safe to modify SceneTree
pass