
Godot Platform Console
- 155 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-platform-console for development tasks
About
godot-platform-console: A skill for development. This provides functionality for development workflows.
- godot-platform-console
Godot Platform Console by the numbers
- 155 all-time installs (skills.sh)
- +16 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #2,447 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-platform-consoleAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 155 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-platform-console for development tasks
Files
Platform: Console
Controller-first design, certification compliance, and locked frame rates define console development.
NEVER Do
- NEVER show a mouse cursor — Showing a cursor on a console is a certification (TRC/TCR) failure. Hide it using
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN). - NEVER skip pausing on focus loss — If a player presses the Home button and the game keeps running, it's a certification violation. Monitor
NOTIFICATION_APPLICATION_FOCUS_OUTand force a pause. - NEVER use an unlocked frame rate — Variable FPS leads to screen tearing and rejections. Lock specifically to 30 or 60 FPS using
Engine.max_fpsand enable VSync. - NEVER forget D-Pad navigation — UI that is only navigable with an analog stick is an accessibility failure. Always support D-Pad inputs for all menu interactions.
- NEVER hardcode button labels — Displaying "Press A" on a PlayStation controller is a major mistake. Use
Input.get_joy_button_string()or dynamic icon mapping based on the controller's GUID. - NEVER exceed hardware memory limits — Consoles (especially Switch) have very rigid RAM budgets. Exceeding them causes OS-level crashes. Profile strictly using the memory tab in the Godot Profiler.
- NEVER assume Joypad 0 is always Player 1 — Users may connect controllers in any order. Always query active connections dynamically using
Input.get_connected_joypads(). - NEVER distribute console export templates or SDKs publicly — Console SDKs are strictly under Non-Disclosure Agreements (NDAs). Leaking these can lead to legal action and developer banishment.
- NEVER handle continuous analog stick input using boolean checks — Using
is_action_pressed()for sticks ignores deadzones and precision. Useget_vector()orget_action_strength()for smooth movement. - NEVER vibrate controllers continuously without an option to disable it — Always use
Input.start_joy_vibration()with finite durations and provide an accessibility toggle to turn off haptics. - NEVER expect traditional OS window manipulation to function on consoles — Consoles operate in fixed fullscreen environments. Methods like
DisplayServer.window_set_mode()will typically fail or be ignored. - NEVER map UI interactions manually to raw button indices — Always utilize the Project Input Map (u_accept, u_cancel). This allows Godot to dynamically route gamepad events regardless of the specific hardware.
- NEVER rely on NOTIFICATION_WM_CLOSE_REQUEST for termination — Consoles often use background suspension (similar to mobile) rather than explicit window closing.
- NEVER query inputs without flushing buffered events for frame-perfect logic — Use
Input.flush_buffered_events()before critical checks to ensure responsiveness. - NEVER use == or != to evaluate analog trigger axes — Floating-point precision loss makes exact equality comparison unreliable. Use
is_equal_approx()for analog values. - NEVER leave orphaned nodes in memory during scene transitions — Strict RAM limits mandate aggressive cleanup. Always use
queue_free()and avoid cyclic references that prevent garbage collection.
---
Available Scripts
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
certification_manager.gd
Expert TRC/TCR compliance (focus loss, controller disconnects).
performance_scaler_fsr.gd
Dynamic Resolution Scaling and FSR 2.2 management for console performance.
server_side_projectile.gd
Direct RenderingServer/PhysicsServer bypass for high-frequency objects.
async_save_manager.gd
Atomic, corruption-resistant threaded save system.
controller_prompt_mapper.gd
GUID-based button prompt detection (PlayStation/Xbox/Switch).
memory_budget_guard.gd
Strict RAM monitoring for platform-specific hardware budgets.
platform_dialog_invoker.gd
Native OS dialog and virtual keyboard abstraction.
background_data_prefetcher.gd
Asset pre-fetching using WorkerThreadPool to avoid level-load stutters.
achievement_offline_queue.gd
Achievement/Trophy caching with offline persistence.
console_boot_config.gd
Hardware-aware hardware initialization and rendering overrides.
---
NEVER Do (Expert Console Rules)
Certification & TRC
- NEVER show a mouse cursor — Rejection (TRC/TCR) failure. Hide using
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN). - NEVER skip pausing on focus loss — Rejection violation. Monitor
NOTIFICATION_APPLICATION_FOCUS_OUT. - NEVER let a controller disconnect go unhandled — Must force pause and show "Reconnect Controller" UI.
Performance & Hardware
- NEVER use an unlocked frame rate — Leads to tearing and instability. Lock specifically (30/60) using
Engine.max_fps. - NEVER exceed hardware memory limits — Switch has rigid RAM budgets. Monitor via
OS.get_static_memory_usage(). - NEVER ignore VSync — Console displays expect VSync. Always enable in
DisplayServer.
Architecture & Safety
- NEVER assume Joypad 0 is Player 1 — Users switch slots. Query
Input.get_connected_joypads()dynamically. - NEVER write to `res://` at runtime — Rejection failure. Exported binaries are read-only. Strictly use
user://. - NEVER save synchronously — Main-thread stutters trigger TCR warnings. Offload to
WorkerThreadPool. - NEVER skip atomic renames for saves — Power loss mid-save must not corrupt data. Write to
.tmpthen rename.
Input Handling
func _input(event: InputEvent) -> void:
if event is InputEventJoypadButton:
match event.button_index:
JOY_BUTTON_A:
on_confirm()
JOY_BUTTON_B:
on_cancel()Performance Requirements
- Locked 30/60 FPS - No drops allowed
- Memory limits - Strict budgets
- Certification testing - QA required
Platform Services
- Achievements/Trophies
- Cloud saves
- Multiplayer matchmaking
- Platform friends
Best Practices
1. Controller-Only - No mouse/keyboard 2. Pause on Focus Loss - Required 3. Save Prompts - Must notify saves 4. Certification - Follow TRCs/TCRs
1. Platform-Overlay-Manager (Native UI Dialogs)
To comply with strict console certification (TRCs), avoid custom UI for critical system messages. Use DisplayServer.dialog_show() to invoke native platform dialogs, ensuring the message is handled by the OS.
class_name PlatformOverlayManager extends Node
## Invokes native OS dialogs for system compliance.
func show_native_alert(title: String, msg: String) -> void:
if DisplayServer.has_feature(DisplayServer.FEATURE_SUBWINDOWS):
# Native OS dialog integration.
DisplayServer.dialog_show(title, msg, ["OK"], _on_dialog_closed)
else:
# Fallback to blocking OS alert.
OS.alert(msg, title)
func _on_dialog_closed(button_index: int) -> void:
print("Native dialog closed: ", button_index)2. Shader-Binary-Caching (RenderingDevice)
Consoles use fixed hardware. Enable rendering/shader_compiler/shader_cache/enabled and use RenderingDevice.shader_compile_binary_from_spirv() to compile GPU-optimized binaries, reducing runtime stuttering.
class_name ConsoleShaderManager extends Node
## Manages GPU-specific shader binary compilation.
func get_device_uuid() -> String:
var rd := RenderingServer.get_rendering_device()
if rd:
# Unique ID for the specific GPU and Driver.
return rd.get_device_pipeline_cache_uuid()
return ""3. Controller-Battery-Telemetry Hook
Monitor controller health using Input.joy_connection_changed and Input.get_joy_info(). While battery level requires a platform-specific GDExtension, the telemetry hook queries the hardware identification string.
class_name ControllerTelemetry extends Node
## Tracks controller states and hardware metadata.
func _ready() -> void:
Input.joy_connection_changed.connect(_on_joy_changed)
func _on_joy_changed(id: int, connected: bool) -> void:
if connected:
var info := Input.get_joy_info(id)
# Tracks hardware GUID and XInput index for telemetry logs.
print("Controller %d: %s" % [id, info.get("xinput_index", "Native")])Reference
- Related:
godot-export-builds,godot-input-handling
Related
- Master Skill: godot-master
class_name AchievementOfflineQueue
extends Node
## Expert Achievement/Trophy caching for Offline-ready consoles.
## Queues unlocks locally and flushes to platform services when online.
var _queue_path: String = "user://achievement_queue.dat"
var _pending_ids: Array[StringName] = []
func _ready() -> void:
_load_queue()
func unlock_achievement(achievement_id: StringName) -> void:
if _is_online():
_push_to_platform(achievement_id)
else:
_pending_ids.append(achievement_id)
_save_queue()
func _is_online() -> bool:
# Check connectivity via platform-specific API
return true
func _push_to_platform(_id: StringName) -> void:
# Native SDK call here
pass
func _save_queue() -> void:
var f = FileAccess.open(_queue_path, FileAccess.WRITE)
if f: f.store_var(_pending_ids)
func _load_queue() -> void:
if FileAccess.file_exists(_queue_path):
var f = FileAccess.open(_queue_path, FileAccess.READ)
_pending_ids = f.get_var()
class_name AsyncSaveManager
extends Node
## Expert Threaded Save System for Console certification.
## Prevents main-thread stutters and implements atomic file writing.
var _save_mutex := Mutex.new()
var _is_saving := false
func execute_save(data: Dictionary, slot: int = 1) -> void:
_save_mutex.lock()
if _is_saving:
_save_mutex.unlock()
return
_is_saving = true
_save_mutex.unlock()
# Offload I/O to background thread pooled workers
WorkerThreadPool.add_task(_write_to_disk.bind(data, slot))
func _write_to_disk(data: Dictionary, slot: int) -> void:
var json_str = JSON.stringify(data)
var final_path = "user://save_slot_%d.dat" % slot
var temp_path = final_path + ".tmp"
var file = FileAccess.open(temp_path, FileAccess.WRITE)
if file:
file.store_string(json_str)
file.close()
# Atomic rename to protect against power-loss corruption
DirAccess.rename_absolute(temp_path, final_path)
_save_mutex.lock()
_is_saving = false
_save_mutex.unlock()
class_name BackgroundDataPrefetcher
extends Node
## Expert Content Delivery: Offloads asset pre-fetching to background threads.
## Ensures smooth level transitions on slow console storage.
func prefetch_assets(paths: Array[String]) -> void:
for path in paths:
# Use WorkerThreadPool to keep the main thread fluid
WorkerThreadPool.add_task(_load_asset.bind(path))
func _load_asset(path: String) -> void:
# ResourceLoader.load() on a background thread pre-fills the internal cache.
# Subsequent calls to 'load()' on the main thread will be instant.
var _res = ResourceLoader.load(path)
print("Console: Prefetched ", path)
## Rule: Only prefetch non-critical assets (SFX, MeshData) to avoid I/O bottlenecks.
class_name ConsoleCertificationManager
extends Node
## Expert handler for TRC/TCR certification compliance.
## Automatically manages focus transitions and controller disconnections.
signal focus_lost
signal focus_gained
signal controller_disconnected(device_id: int)
func _ready() -> void:
# TCR: Monitor joypad connectivity changes during runtime
Input.joy_connection_changed.connect(_on_joy_connection_changed)
process_mode = Node.PROCESS_MODE_ALWAYS
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_FOCUS_OUT:
# TRC: System menu opened (Overlay). Must pause immediately.
_enforce_system_pause()
focus_lost.emit()
NOTIFICATION_APPLICATION_FOCUS_IN:
focus_gained.emit()
func _on_joy_connection_changed(device: int, connected: bool) -> void:
if not connected:
# TRC: Controller disconnect must trigger a pause/overlay
_enforce_system_pause()
controller_disconnected.emit(device)
func _enforce_system_pause() -> void:
if not get_tree().paused:
get_tree().paused = true
print("Console: Forced system pause due to focus loss or disconnect.")
class_name ConsoleBootConfig
extends Node
## Expert hardware-aware boot configuration.
## Disables expensive PC-only rendering features on initialization.
func _ready() -> void:
if OS.has_feature("mobile") or OS.has_feature("switch"):
_optimize_for_low_end()
# TRC Requirement: Always enable VSync for consoles
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
func _optimize_for_low_end() -> void:
# Disable real-time global illumination for performance
RenderingServer.gi_set_use_half_resolution(true)
# Lock FPS to prevent heating
Engine.max_fps = 30
# skills/platform-console/scripts/console_compliance_handler.gd
extends Node
## Console Compliance Handler Expert Pattern
## Automates TRC/TCR requirements: Focus loss handling, User ID checks, Save indicators.
class_name ConsoleComplianceHandler
signal focus_lost
signal focus_gained
@export var pause_on_focus_loss: bool = true
@export var show_mouse_cursor: bool = false
@export var save_icon: CanvasItem # Optional reference to UI icon
var _is_saving: bool = false
func _ready() -> void:
# 1. Cursor Management
if not show_mouse_cursor:
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
# 2. V-Sync Enforcement
# Consoles typically mandate V-Sync to prevent tearing
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
print("[ConsoleCompliance] Initialized. Mouse Hidden: ", !show_mouse_cursor)
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_FOCUS_OUT:
_handle_focus_loss()
NOTIFICATION_APPLICATION_FOCUS_IN:
_handle_focus_gain()
func _handle_focus_loss() -> void:
print("[ConsoleCompliance] Focus LOST")
focus_lost.emit()
if pause_on_focus_loss:
# TRC Requirement: Game must pause entirely when system focus is lost
get_tree().paused = true
# Usually we show a "Press Start to Resume" screen or modal here
func _handle_focus_gain() -> void:
print("[ConsoleCompliance] Focus GAINED")
focus_gained.emit()
# TRC Requirement: Do not auto-unpause if the game was paused by player before focus loss.
# Best practice: Remain paused and wait for user input.
func notify_save_start() -> void:
_is_saving = true
# TRC Requirement: Indicate clearly when saving is happening
if save_icon:
save_icon.show()
# Platform-specific "Saving..." overlay call could go here
func notify_save_end() -> void:
_is_saving = false
if save_icon:
save_icon.hide()
## EXPERT USAGE:
## ComplianceHandler.notify_save_start() -> await save() -> notify_save_end()
class_name ControllerPromptMapper
extends RefCounted
## Expert GUID-based Icon Routing for Console UI.
## Detects hardware type to display correct button prompts (PS/Xbox/Switch).
static func get_prompt_path(device_id: int) -> String:
var guid = Input.get_joy_guid(device_id)
var name = Input.get_joy_name(device_id).to_lower()
# Detect platform from standardized SDL2 identifiers
if "nintendo" in name or "switch" in name:
return "res://ui/prompts/nintendo_set.tres"
elif "ps4" in name or "ps5" in name or "dual" in name:
return "res://ui/prompts/playstation_set.tres"
else:
# Fallback to Xbox/XInput standard
return "res://ui/prompts/xbox_set.tres"
## Rule: Always display SVG-based prompts for high-DPI console displays.
class_name MemoryBudgetGuard
extends Node
## Expert RAM Monitoring for Console-specific budgets (e.g. Nintendo Switch).
## Triggers aggressive resource cleanup when thresholds are reached.
@export var ram_limit_mb: int = 1500 # Adjust per platform budget
@export var cleanup_threshold_pct: float = 0.85
func _ready() -> void:
# Check memory periodically
var timer = Timer.new()
timer.wait_time = 5.0
timer.autostart = true
timer.timeout.connect(_check_memory)
add_child(timer)
func _check_memory() -> void:
var usage_mb = OS.get_static_memory_usage() / 1024 / 1024
if usage_mb > (ram_limit_mb * cleanup_threshold_pct):
_trigger_emergency_cleanup()
func _trigger_emergency_cleanup() -> void:
print("Console: RAM budget reached threshold. Clearing caches.")
# Expert: Flush ResourceLoader cache and force GC
# Note: This is a heavy operation, only use as a fail-safe.
pass
class_name PerformanceScalerFSR
extends Node
## Expert Dynamic Resolution Scaling and FSR 2.2 management.
## Optimized for weak hardware (Nintendo Switch) using temporal upscaling.
func apply_performance_profile(viewport: Viewport, profile: StringName = &"balanced") -> void:
# Forward+ renderer supports FSR2 natively
viewport.scaling_3d_mode = Viewport.SCALING_3D_MODE_FSR2
match profile:
&"performance":
viewport.scaling_3d_scale = 0.5 # 540p -> 1080p
viewport.fsr_sharpness = 0.4
&"balanced":
viewport.scaling_3d_scale = 0.67 # ~720p -> 1080p
viewport.fsr_sharpness = 0.2
&"quality":
viewport.scaling_3d_scale = 0.85
viewport.fsr_sharpness = 0.1
## Expert: Lower mipmap bias automatically follows scaling_3d_scale in Godot 4.
# platform_console_patterns.gd
extends Node
# 1. Dynamically Polling Active Controllers
# EXPERT NOTE: Joypad 0 is not always Player 1. Always query active connections.
func get_active_players() -> Array[int]:
return Input.get_connected_joypads()
# 2. Extracting Analog Stick Vectors with Deadzones
# EXPERT NOTE: Accounts for hardware drift automatically across multiple axis.
func get_movement_vector() -> Vector2:
return Input.get_vector(&"move_left", &"move_right", &"move_up", &"move_down")
# 3. Applying Controller Haptics
# EXPERT NOTE: Finite duration prevents motor burnout and respects hardware limits.
func trigger_damage_feedback(device_id: int) -> void:
# device_id, weak_motor, strong_motor, duration_sec
Input.start_joy_vibration(device_id, 0.5, 1.0, 0.2)
# 4. Looking up Controller GUIDs for Profiling
# EXPERT NOTE: Use for hardware-specific mapping adjustments (SDL2 compatible).
func get_gamepad_profile(device_id: int) -> String:
return Input.get_joy_guid(device_id)
# 5. programmatic UI Focus for Gamepads
# EXPERT NOTE: Essential for console UI accessibility.
func grab_initial_menu_focus(container: Control) -> void:
var first_btn := container.get_child(0) as Control
if first_btn:
first_btn.grab_focus()
# 6. Adjusting 3D Resolution Scaling (FSR)
# EXPERT NOTE: Maintain 60 FPS on lower-tier console hardware.
func enable_performance_scaling(viewport: Viewport) -> void:
viewport.scaling_3d_mode = Viewport.SCALING_3D_MODE_FSR
viewport.scaling_3d_scale = 0.75 # Sub-native render, upscale with FSR
# 7. Exact Input Matching
# EXPERT NOTE: Prevents overlapping action triggers in complex mappings.
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed(&"jump", false, true): # Exact match
print("Jump executed!")
# 8. Setting up Dynamic UI Neighbors
# EXPERT NOTE: Manually override focus flow for non-standard layouts.
func link_ui_nodes(btn: Control, neighbor_path: NodePath) -> void:
btn.set_focus_neighbor(SIDE_BOTTOM, neighbor_path)
# 9. Frame-Perfect Input Interception
# EXPERT NOTE: Flush the buffer before critical time-sensitive checks.
func process_combat_frame() -> void:
if Input.is_action_just_pressed(&"attack"):
Input.flush_buffered_events()
# logic...
# 10. Assigning Multi-Player Authority
# EXPERT NOTE: Map inputs to specific players in local coop or split-screen.
func setup_player_authority(player_node: Node, device_id: int) -> void:
player_node.set_multiplayer_authority(device_id)
class_name PlatformDialogInvoker
extends Node
## Abstract interface for native Console dialogs (Keyboard, Prompts).
## Detects the platform and routes to the appropriate OS handler.
func show_virtual_keyboard(title: String, existing_text: String = "") -> void:
if OS.has_feature("mobile") or OS.has_feature("console"):
# Expert: DisplayServer.virtual_keyboard_show handles most platform-native input
DisplayServer.virtual_keyboard_show(existing_text, Rect2(0,0,0,0))
else:
# PC Fallback for dev testing
pass
func show_system_dialog(title: String, message: String) -> void:
# Implementation varies by native GDExtension bridge
pass
class_name ServerSideProjectile
extends RefCounted
## Expert Object Management: Bypassing the SceneTree using Server APIs.
## Offloads CPU transform propagation for thousands of entities.
var _mesh_instance_rid: RID
var _body_rid: RID
func _init(world_2d_or_3d_rid: RID, scenario_rid: RID, mesh_rid: RID, shape_rid: RID) -> void:
# Directly allocate physics on the C++ PhysicsServer
_body_rid = PhysicsServer3D.body_create() # Change to 2D if needed
PhysicsServer3D.body_set_space(_body_rid, world_2d_or_3d_rid)
PhysicsServer3D.body_add_shape(_body_rid, shape_rid)
# Directly allocate visuals on the GPU RenderingServer
_mesh_instance_rid = RenderingServer.instance_create()
RenderingServer.instance_set_base(_mesh_instance_rid, mesh_rid)
RenderingServer.instance_set_scenario(_mesh_instance_rid, scenario_rid)
func update_transform(new_transform: Transform3D) -> void:
# O(1) direct memory access, bypassing Node traversal
PhysicsServer3D.body_set_state(_body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, new_transform)
RenderingServer.instance_set_transform(_mesh_instance_rid, new_transform)
func destroy() -> void:
PhysicsServer3D.free_rid(_body_rid)
RenderingServer.free_rid(_mesh_instance_rid)