# Current Best Practices — Unity 6 + FishNet v4

## Networking (FishNet v4)

### Lifecycle

- Use `OnStartServer()` / `OnStartClient()` / `OnStartNetwork()` — never `Start()` or `Awake()` for networked logic
- Use `OnStopServer()` / `OnStopClient()` for cleanup
- Guard owner-only code: `if (!IsOwner) return;`
- Never use `IsOwner` inside `OnStartServer()` — use `Owner.IsLocalClient` instead

### Prediction

- Implement `IReplicateData` / `IReconcileData` on input/state structs
- Hook `TimeManager.OnTick += TimeManager_OnTick;` in `OnStartNetwork()`
- Unhook in `OnStopNetwork()`
- Zero GC allocations in the prediction loop
- Reconcile tolerance: 0.1m before position snap

### RPCs

- `[ServerRpc]` — client → server
- `[ObserversRpc]` — server → all observers
- `[TargetRpc]` — server → specific client
- Always validate inputs in ServerRpc handlers (server-authoritative)

### SyncTypes

- `SyncVar<T>` for single values with change callbacks
- `SyncList<T>` for collections
- Set in server code only — clients read

### Spawning

- Always spawn via `ServerManager.Spawn(obj, owner)` on server
- Despawn via `ServerManager.Despawn(obj)` — never `Destroy()`
- Wait for `LoadedStartScenes()` before spawning for a connection

## Physics

- Use `LayerMask.GetMask("LayerName")` — never hardcode layer integers
- Cache layer masks as `static readonly int`
- Use `Physics.SphereCast` / `Physics.Raycast` with explicit masks (ADR-0006)
- `CharacterController.Move()` for player movement (not Rigidbody)

## UI (uGUI)

- Screen Space Overlay for HUDs
- `CanvasGroup` for fade animations
- `DOTween` for all animated transitions
- Owner-only instantiation: check `IsOwner` before creating HUD

## ScriptableObjects

- Define data schema as `[CreateAssetMenu]` ScriptableObjects
- Load at runtime via `Resources.LoadAll<T>("path")`
- Never pass SO references across the network — use string IDs
- All tuning constants in `GlobalRules_SO`

## Performance

- Avoid `Update()` where FishNet `OnTick` suffices
- Pre-allocate arrays for physics queries (`RaycastHit[]`)
- Use `NonAlloc` variants: `Physics.RaycastNonAlloc`, `Physics.SphereCastNonAlloc`
- Cache `Transform` references in `OnStartNetwork()`
- Use `TryGetComponent<T>()` over `GetComponent<T>()` + null check

## Scenes

- Two scenes: LobbyScene + GameScene
- `DontDestroyOnLoad` singletons: RelayClient, LobbyManager, NetworkManager, AudioManager
- Load game scene via `SceneManager.LoadSceneAsync()` — FishNet starts AFTER load completes
- Static `GameStartData` class for cross-scene data transfer
