KorvayneGuides
Unity

Unity integration

The Unity sample wraps the native SDK with C# classes so gameplay scripts use simple manager and protected value APIs.

Copy wrapper

  1. Copy samples/unity/Assets/Korvayne into the Unity project's Assets folder.
  2. Add an empty GameObject to the first scene.
  3. Attach KorvayneManager.
  4. Set game ID, environment, provider, and build context in the Inspector or at runtime.

Runtime files

For packaged Windows builds, place these next to the final game executable:

YourUnityGame.exe
anticheat.dll
anticheat.lic
anticheat.ini

Unity native plugin folders can help editor testing, but the license and config location for the shipped game is next to the final executable.

Protected values

Use the provided wrappers instead of guarding ordinary managed fields directly.

private KorvayneProtectedFloat health;

void Start()
{
    health = new KorvayneProtectedFloat("player.health", 100f);
}

void ApplyDamage(float damage)
{
    health.Set(health.Get() - damage);
}

The Unity/Mono/.NET garbage collector can move managed objects. The wrapper keeps the native SDK interaction stable.

Protected saves

Serialize the save normally, then pass the byte array through the wrapper before writing it to disk. Verify it before deserializing during load.

var context = $"player={playerId};slot=profile;schema=1";
var bytes = Encoding.UTF8.GetBytes(json);

int rc = KorvayneSdk.ProtectSaveFile(path, bytes, context);
if (KorvayneSdk.TryVerifySaveFile(path, context, out var verified, out rc)) {
    var loadedJson = Encoding.UTF8.GetString(verified);
}

Use KorvayneSdk.SaveResultName(rc) for logs and reject the load when verification returns a tamper result.

Report shots

Aim behavior reports are optional. The SDK does not read mouse input, target positions, or visibility state by itself. Unity gameplay code should call the wrapper once after the local player's weapon logic has resolved whether the shot hit and whether line of sight was valid.

KorvayneManager.Instance.ReportShot(
    aimSpeedPerMs,
    reactionMs,
    hadLineOfSight,
    hit
);
ValueWhere to get it
aimSpeedPerMsStore the local camera or aim transform rotation from the previous weapon tick, compare it with the rotation at fire time, then divide by elapsed milliseconds.
reactionMsWhen your visibility, target selection, raycast, or crosshair logic first marks a target as valid, store Time.time. On shot, send the time difference in milliseconds.
hadLineOfSightReuse the same raycast or weapon-validation result your game already uses for visibility.
hitUse the final local result after the shot has resolved, not an early prediction.

Safe fallback: leave ReportShot out until the game can provide real values. Sending placeholders is worse than not sending the optional behavior signal.

  • Call it once per local human-player shot, not from Update().
  • Do not report AI, remote players, spectators, or replay playback.
  • Keep behavior_response = report until normal gameplay telemetry has been reviewed.

Those reports can become advisory AimSnap, Triggerbot, or Wallhack evidence in logs or studio telemetry when the supplied shot values cross simple built-in thresholds.

EvidenceCurrent condition
AimSnapHit shot with aimSpeedPerMs > 8.0.
TriggerbotThree hit shots with reaction time below 60ms; normal reactions above 120ms reset the counter.
WallhackHit shot while hadLineOfSight is false.

Telemetry for these events is controlled by [TelemetryEvents] aim_behavior; local action is controlled by [Enforcement] behavior_response. Keep this report-only until real player baselines are reviewed.

Common mistakes

  • Putting anticheat.lic in Assets but not next to the final executable.
  • Calling protected values before Korvayne startup has completed.
  • Changing protected gameplay fields directly after wrapping them.
  • Calling shot reports for bots or replicated remote players.
  • Shipping backend secrets in scripts, Resources, ScriptableObjects, or config files.