From per-aspera-modding
Validated IL2CPP code patterns for Per Aspera modding. Use when unsure whether a Unity/IL2CPP pattern works, looking up MonoBehaviour + [RegisterInIl2Cpp], UnityEngine.Input, KeyCode, System.Type safety rules, Mirror/SingletonMirror patterns, SceneManager integration, or checking if a claimed limitation is real or an LLM hallucination.
How this skill is triggered — by the user, by Claude, or both
Slash command
/per-aspera-modding:per-aspera-code-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
All patterns below have been verified working in production Per Aspera mods.
All patterns below have been verified working in production Per Aspera mods.
// Evidence: CommandsDemo.cs L97-106
using UnityEngine;
private void Update()
{
if (UnityEngine.Input.GetKeyDown(KeyCode.F9))
{
TriggerAction();
}
}
✅ Works in IL2CPP. Direct Unity Input, no wrappers needed.
using UnityEngine;
using Il2CppInterop.Runtime.Injection;
[RegisterInIl2Cpp]
public class MyGameComponent : MonoBehaviour
{
public MyGameComponent(System.IntPtr ptr) : base(ptr) { }
private void Awake() { /* lifecycle works */ }
private void Update() { /* lifecycle works */ }
private void OnDestroy() { /* lifecycle works */ }
}
✅ Full Unity lifecycle support. Required pattern for all interactive components.
using BepInEx;
using BepInEx.Unity.IL2CPP;
[BepInPlugin("com.namespace.modname", "Display Name", "1.0.0")]
public class MyPlugin : BasePlugin
{
public override void Load()
{
Log.LogInfo("Plugin loaded!");
AddComponent<MyGameComponent>(); // Attach MonoBehaviour
}
}
✅ Required pattern. Always use
BasePlugin— neverBaseUnityPluginin IL2CPP.
Résolu au build (2026-06) : alias global
Type = System.TypedansDirectory.Build.props(racine + SDK).Typenu compile et signifieSystem.Typepartout dans ce workspace — plus besoin de qualifier manuellement.
// ✅ Both correct in this workspace
private static Type? _buildingType; // alias → System.Type
private static System.Type? _cargoType; // explicite — OK aussi
// Hors workspace (projet sans l'alias) : qualifier System.Type ou copier l'alias :
// <Using Include="System.Type" Alias="Type" />
⚠️ Réflexion (
GetMethod/GetField…) horsPerAspera.Core.IL2CppExtensions: warning RS0030 (BannedApiAnalyzers). Préférer les proxies interop typés.
using PerAspera.Core.IL2CPP;
// Read field or property
float? temp = instance.GetMemberValue<float>("averageTemperature");
string name = instance.GetMemberValue<string>("displayName");
// Invoke with parameters
float? result = instance.InvokeMethod<float>("GetAverageTemperature");
bool? canBuild = instance.InvokeMethod<bool>("CanPlaceBuilding", buildingType, pos);
// Write value
instance.SetMemberValue("targetTemperature", 25.5f);
// Untyped fallback
var state = instance.SafeInvoke("GetCurrentState");
using PerAspera.GameAPI.Wrappers;
var currentScene = SceneManager.GetActiveScene();
var allScenes = SceneManager.GetAllScenes();
SceneManager.SceneLoaded += (scene, mode) =>
{
var roots = scene.GetRootGameObjects();
foreach (var obj in roots)
{
// safe to process
}
};
| Claim | Reality |
|---|---|
| "UnityEngine.Input not available in IL2CPP" | ✅ Works perfectly |
| "Need Il2Cpp wrappers for Unity classes" | ✅ Direct Unity classes work |
| "KeyCode enum not supported" | ✅ Standard Unity enums work normally |
| "MonoBehaviour requires special setup" | ✅ Just add [RegisterInIl2Cpp] and IntPtr ctor |
"Type is fine in IL2CPP" | ✅ Dans ce workspace oui (alias global Type=System.Type) ; ailleurs, qualifier System.Type |
"Use ?? between FieldInfo and PropertyInfo" | ❌ CS0019 — use separate if blocks |
"BaseGame.Instance accesses the singleton" | ❌ N'existe PAS — utiliser BaseGame.self (champ static) ou BaseGame.Get() |
| "Atmosphere.cs contient les données climatiques" | ❌ VISUEL ONLY (shader) — les données sont dans Planet.cs fields |
using PerAspera.GameAPI.Mirror;
// Basic Mirror
public class MirrorPlanet : Mirror<object>
{
public float? GetTemperature() => Invoke<float>("GetAverageTemperature");
public string GetName() => GetField<string>("planetName");
}
// Singleton Mirror
public class MirrorBaseGame : SingletonMirror<MirrorBaseGame, object>
{
// ✅ BaseGame uses static field 'self' — NOT BaseGame.Instance (n'existe pas dans le source IL2CPP)
protected override object GetInstanceInternal() => BaseGame.self;
public float GetDay() => Invoke<float>("GetCurrentDay") ?? 0f;
}
// Access
var temp = MirrorUniverse.GetPlanet()?.GetTemperature();
npx claudepluginhub perasperamods/per-aspera-skills --plugin per-aspera-moddingProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.