# Predicted Identities

Predicted identities are regular Unity components that PurrDiction simulates deterministically. The system snapshots and reconciles their `STATE` against server authority, while your code focuses on clean, single‑player‑style logic.

**Variants**

* `PredictedIdentity<STATE>`
  * For stateful systems without direct player input (AI, timers, pooled objects).
  * Implement: `GetInitialState`, `GetUnityState`, `SetUnityState`, `Simulate`, `LateSimulate` (optional), `UpdateView` (optional).
* `PredictedIdentity<INPUT, STATE>`
  * Adds an `INPUT` pipe for local/remote control. Implement: `GetFinalInput`, `UpdateInput` (optional), `SanitizeInput`, `Simulate(INPUT, ref STATE, float)`, `ModifyExtrapolatedInput` (optional).
* `StatelessPredictedIdentity`
  * For pure event/logic systems that don’t carry custom state. Override `Simulate(float delta)`.

Both `INPUT` and `STATE` must be structs that implement the appropriate prediction interfaces (`IPredictedData`, `IPredictedData<T>`).

***

**Lifecycle Hooks**

* `LateAwake()` — Called once after fresh spawn initialization. View‑only setup is appropriate here.
* `SimulationStart()` — First tick only, before simulation begins. Good for caching or one‑time transitions.
* `Simulate(...)` — Deterministic simulation each tick. Use only `STATE`/`INPUT` and deterministic data.
* `LateSimulate(...)` — Optional late pass after `Simulate` each tick (e.g., composing derived values).
* `Destroyed()` — Called when despawning/cleanup occurs (pool or destroy).
* `ResetState()` — Clears ownership/IDs and resets interpolation and internal caches for pooling.

Unity bridging:

* `GetUnityState(ref STATE state)` — Read Unity components into `STATE`.
* `SetUnityState(STATE state)` — Apply `STATE` back to Unity components after rollback.

View & interpolation:

* `UpdateView(STATE viewState, STATE? verified)` — Render using the current interpolated `viewState`. `verified` holds the last server snapshot, when present.
* `ResetInterpolation()` — Clear internal smoothing/error.

***

**Ownership and Control**

* `owner` — Optional `PlayerID` who owns this identity.
* `isOwner` — True if `owner == PredictionManager.localPlayer` on this client.
* `isController` — True for the owner on clients; on server, also true for bots/AI cases.
* `OnViewOwnerChanged(oldOwner, newOwner)` — View‑only callback when ownership changes; do not mutate simulation here.

Use these flags for visuals (camera, highlights, UI). Keep simulation deterministic and independent of local presentation.

***

**Example Skeleton (Stateful with Input)**

```csharp
public struct MyInput : IPredictedData {
    public Vector2? input; public void Dispose() {}
}

public struct MyState : IPredictedData<MyState> {
    public Vector3 pos; public Quaternion rot; public void Dispose() {}
}

public class MyPredicted : PredictedIdentity<MyInput, MyState>
{
    protected override MyState GetInitialState() => new MyState {
        pos = transform.position, rot = transform.rotation
    };

    protected override void GetUnityState(ref MyState s)
    { s.pos = transform.position; s.rot = transform.rotation; }

    protected override void SetUnityState(MyState s)
    { transform.SetPositionAndRotation(s.pos, s.rot); }

    protected override void GetFinalInput(ref MyInput i)
    { i.input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")); }

    protected override void SanitizeInput(ref MyInput i)
    { 
        if (!i.input.HasValue) return;
        var v = Vector2.ClampMagnitude(new Vector2(i.input.Value.x, i.input.Value.y), 1f); 
        i.input = v; 
    }

    protected override void Simulate(MyInput i, ref MyState s, float dt)
    {
        if (!i.input.HasValue) return;
        transform.position += new Vector3(i.input.Value.x, 0, i.input.Value.y) * dt * 5f;
    }

    protected override void UpdateView(MyState view, MyState? verified)
    { transform.SetPositionAndRotation(view.pos, view.rot); }
}
```
