Safe Natives — SafeNatives and CrowdNatives
Anything outside the verified RPH managed surface is a raw game native, called by hash.
A wrong hash or changed signature normally means a crashed plugin — or a crashed game.
Burnsie.Common.Natives fences every such call:
- Every native the library touches is called by a pinned hash (confirmed against alloc8or's natives.json).
- The first failure of a native logs once and disables that native for the rest of the session. Subsequent calls return defaults silently.
- Nothing here ever throws. A bad native degrades one effect instead of taking the scene or the plugin down.
Set the logger once at plugin startup so failures are reported:
SafeNatives.Log = log;
CrowdNatives.Log = log;
All calls must run on a GameFiber. Ped/entity parameters are null- and
Exists()-guarded inside the wrappers.
SafeNatives — general-purpose natives
Feedback & effects
SafeNatives.PlayFrontendSound("ERROR", "HUD_FRONTEND_DEFAULT_SOUNDSET"); // or use FrontendSounds
SafeNatives.AnimPostFxPlay("DeathFailMPIn", 0, looped: true); // full-screen effect; 0 = until stopped
SafeNatives.AnimPostFxStop("DeathFailMPIn");
SafeNatives.AnimPostFxStopAll(); // teardown safety
SafeNatives.ShakeGameplayCam("JOLT_SHAKE", 0.4f);
SafeNatives.StopGameplayCamShaking();
SafeNatives.PadShake(durationMs: 400, frequency: 200); // controller rumble (freq 0–255)
SafeNatives.StopPadShake();
SafeNatives.FlashMinimap(); // one flash, activation emphasis
Ped state & tasks
SafeNatives.ApplyPedDamagePack(ped, "SCR_Torture", 0f, 1f); // visible damage
SafeNatives.ResetPedVisibleDamage(ped); // revive path
SafeNatives.SetPedToRagdoll(ped, 3000); // limp ragdoll; re-call to hold down
SafeNatives.ClearPedTasks(ped); // graceful
SafeNatives.ClearPedTasksImmediately(ped); // instant (stands up out of ragdoll)
SafeNatives.TaskPlayAnim(ped, dict, anim, flag: 1); // flag 1 = loop until cleared
SafeNatives.TaskFollowNavMeshToCoord(ped, target, moveBlend: 2f, targetRadius: 1.5f); // 1=walk 2=run
SafeNatives.TaskTurnPedToFaceEntity(ped, target, 2000);
SafeNatives.TaskLeaveVehicle(ped, vehicle);
bool inVeh = SafeNatives.IsPedInAnyVehicle(ped);
int type = SafeNatives.GetPedType(ped); // 6=cop, 20=medic, 21=firefighter; -1 unavailable
Animation dictionaries stream asynchronously — request, poll, release:
SafeNatives.RequestAnimDict(dict);
while (!SafeNatives.IsAnimDictLoaded(dict)) GameFiber.Sleep(50); // add a deadline
SafeNatives.TaskPlayAnim(ped, dict, anim, 1);
// later: SafeNatives.RemoveAnimDict(dict);
Player & death-flow control
RPH 1.124's managed Game.DisableAutomaticRespawn / Game.FadeScreenOutOnDeath throw
NotImplementedException at runtime (observed in the field) — these natives are the
working path for custom death handling:
bool ok = SafeNatives.PauseDeathArrestRestart(true); // dead player stays where they fell
SafeNatives.SetFadeOutAfterDeath(false);
SafeNatives.ResurrectLocalPlayer(position, heading); // standard revive (works in SP)
SafeNatives.SetEntityInvincible(entity, true); // ALWAYS pair with (entity, false)
SafeNatives.SetTimeScale(1.0f); // cancels death slow-mo
SafeNatives.SetPlayerDamageResistance(player, 0.5f); // sets weapon AND melee modifiers
bool faded = SafeNatives.IsScreenFadedOut();
SafeNatives.DoScreenFadeIn(800);
Two contracts to respect:
PauseDeathArrestRestartreturnsfalsewhen the native is unavailable. If you build death handling on top, honor that return — arming halfway leaves vanilla respawn running underneath yours.- There is no getter for these states: restore game defaults (restart running, fade-out on) at teardown rather than a cached value.
Input
bool typing = SafeNatives.IsOnScreenKeyboardActive(); // used by Keybind suppression
SafeNatives.SetPlayerControl(player, false); // lock movement input for scripted
// scenes (camera stays free).
// ALWAYS pair with (player, true) on
// EVERY exit path — a stuck lock is
// a soft-lock. First consumer:
// PanicButton's CPR patient pose.
CrowdNatives — crowd/scene control natives
Same fence, same rules; used by the Crowd engine and available to your scenes directly.
// Density suppression — these are PER-FRAME natives; call every tick while suppressing:
CrowdNatives.SuppressPedDensityThisFrame(0.1f);
CrowdNatives.SuppressScenarioDensityThisFrame(0.1f);
CrowdNatives.SuppressVehicleDensityThisFrame(0.1f);
// Scene preparation:
CrowdNatives.ClearAreaOfPeds(position, radius: 40f); // one-time clear
int areaId = CrowdNatives.AddScenarioBlockingArea(min, max); // stop ambient scenario spawns
CrowdNatives.RemoveScenarioBlockingArea(areaId); // per-scene cleanup (id 0 = no-op)
CrowdNatives.RemoveAllScenarioBlockingAreas(); // LAST-RESORT unload sweep only —
// removes other plugins' areas too
// Fire:
int fireId = CrowdNatives.StartScriptFire(position, maxChildren: 10, gasFire: false);
CrowdNatives.StopFireInRange(position, radius: 30f);
// maxChildren clamps to 0–25; respect the game's global 128-fire cap.
// Ped behavior:
CrowdNatives.StartScenarioInPlace(ped, "WORLD_HUMAN_STAND_MOBILE"); // idle crowd texture
CrowdNatives.WanderInArea(ped, center, radius: 10f);
CrowdNatives.SmartFleePed(ped, fleeFrom, safeDistance: 60f, fleeTimeMs: -1);
CrowdNatives.SetFleeAttributes(ped, CrowdNatives.FleeAttributeForceCower, true);
CrowdNatives.SetCombatAttribute(ped, 17, true); // 17 = always flee
CrowdNatives.StandGuard(cop, position, heading); // cordon post
// Environment:
CrowdNatives.SetArtificialLights(on: false); // blackout. Wrapper negates the native's
// inverted parameter, so call sites read honestly.
CrowdNatives.SetRoadsInArea(min, max, false); // reroute ambient traffic around a scene
CrowdNatives.SetRoadsInArea(min, max, true); // ALWAYS restore on teardown
Rules
- Prefer the RPH managed API when it exists and works; the fence is for everything else.
- Restore what you set: invincibility off, time scale 1.0, roads re-enabled, lights on, post-effects stopped — on every exit path.
- Per-scene resources (scenario blocking areas) are removed by id, never with the global sweep, so you never touch other plugins' state.
- A
default-value return can mean either "the native said no" or "the native is disabled". Where the difference matters, the wrapper returnsboolsuccess — check it.