uw-network-setup-e6063b — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited uw-network-setup-e6063b (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Generate networking code adapted to the project's chosen package.
docs/ProjectConfig.yaml for:networking — the package name ("ngo", "mirror", "photon", or "none"). If "none", stop — do not generate networking code.architecture_pattern — "so-first" or "di-first" (affects how network services are wired).mcp.unity_mcp — if true, call refresh_unity after creating files.docs/TDD_Template.md for the authority matrix (who owns what data).docs/CODING_STANDARDS.md for async patterns (Awaitable + CancellationToken) and class structure.docs/NAMING_CONVENTIONS.md for file/class naming.Each package has a detailed reference file. Read the relevant one before generating code:
NetworkVariable, [Rpc] attributes.[SyncVar], [Command]/[ClientRpc].IPunObservable, [PunRPC].Every package has a mechanism for syncing state from server/host to clients. The pattern is the same — only the syntax differs.
| Package | Sync Mechanism | Change Callback | Example |
|---|---|---|---|
| NGO | NetworkVariable<T> | .OnValueChanged += handler | _health.OnValueChanged += OnHealthChanged; |
| Mirror | [SyncVar(hook = nameof(...))] | Hook method | [SyncVar(hook = nameof(OnHealthChanged))] |
| Photon | IPunObservable | OnPhotonSerializeView | Manual read/write in serialize callback |
RPCs let clients request actions on the server, or the server push updates to clients.
NGO:
[Rpc(SendTo.Server)]
public void RequestDamageServerRpc(int amount)
{
// Runs on server — validate before applying
if (amount < 0 || amount > MAX_DAMAGE) return;
_health.Value = Mathf.Max(0, _health.Value - amount);
}
[Rpc(SendTo.ClientsAndHost)]
public void PlayHitEffectClientRpc()
{
// Runs on all clients — visual/audio only, no game state
}Mirror:
[Command]
public void CmdRequestDamage(int amount)
{
if (amount < 0 || amount > MAX_DAMAGE) return;
_health = Mathf.Max(0, _health - amount);
RpcPlayHitEffect();
}
[ClientRpc]
public void RpcPlayHitEffect()
{
// Visual/audio feedback on all clients
}Photon:
public void RequestDamage(int amount)
{
photonView.RPC(nameof(RpcTakeDamage), RpcTarget.MasterClient, amount);
}
[PunRPC]
private void RpcTakeDamage(int amount, PhotonMessageInfo info)
{
if (!PhotonNetwork.IsMasterClient) return;
if (amount < 0 || amount > MAX_DAMAGE) return;
_health = Mathf.Max(0, _health - amount);
}Spawning networked objects follows the same principle: only the server/host creates the authoritative instance, and the network layer replicates it to clients.
NGO:
// Server only — spawn a networked prefab
var instance = Instantiate(_projectilePrefab, spawnPoint.position, spawnPoint.rotation);
instance.GetComponent<NetworkObject>().Spawn();Mirror:
// Server only
var instance = Instantiate(_projectilePrefab, spawnPoint.position, spawnPoint.rotation);
NetworkServer.Spawn(instance);Photon:
// Room owner or any client (Photon handles ownership)
PhotonNetwork.Instantiate(_projectilePrefabName, spawnPoint.position, spawnPoint.rotation);Define who owns, reads, and writes each piece of networked state. Fill this in before writing code — it prevents authority bugs.
| Entity | Owner | Read | Write | Sync Method | Validation |
|---|---|---|---|---|---|
| Player Health | Server | Everyone | Server | NetworkVariable / SyncVar | Clamp 0-max, rate limit |
| Player Position | Server | Everyone | Server | NetworkTransform | Speed cap, teleport check |
| Player Input | Client | Server | Owner | RPC (client -> server) | Sanitize on server |
| Chat Messages | Client | Everyone | Owner | RPC | Length limit, rate limit |
| Game Timer | Server | Everyone | Server | NetworkVariable / SyncVar | Server-authoritative only |
| Inventory | Server | Owner | Server | RPC + NetworkVariable | Validate item exists |
When the project might switch networking packages later, or when using DI (architecture_pattern: "di-first"), wrap networking behind an interface:
public interface INetworkService
{
bool IsServer { get; }
bool IsClient { get; }
bool IsConnected { get; }
void SendToServer<T>(T message) where T : struct;
void SendToClients<T>(T message) where T : struct;
}Register the concrete implementation in the DI container (see uw-dependency-injection). Game systems depend on INetworkService, not the package directly. This makes testing easier too — mock the interface in EditMode tests.
When syncing custom structs over the network, each package has its own serialization approach:
NGO — implement INetworkSerializable:
public struct DamageEvent : INetworkSerializable
{
public int Amount;
public ulong AttackerId;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref Amount);
serializer.SerializeValue(ref AttackerId);
}
}Mirror — implement custom Reader/Writer extensions or use [SyncVar] with supported types.
Photon — serialize manually in OnPhotonSerializeView or register custom types with PhotonPeer.RegisterType.
For responsive gameplay (especially movement and shooting), client-side prediction avoids waiting for server confirmation:
This is complex. NGO has built-in NetworkTransform prediction. Mirror has community prediction examples. Photon Fusion has built-in state authority with rollback. For custom prediction, start with movement only and expand from there.
architecture_pattern: "di-first", use uw-dependency-injection to register network services in the container.uw-unity-test-runner — test game logic in EditMode by mocking INetworkService. Test actual networking in PlayMode.docs/TDD_Template.md with the authority matrix before writing any sync code.docs/TDD_Template.md explicitly specifies peer-to-peer.NetworkBehaviour references in Awake() or OnNetworkSpawn() — never GetComponent in Update.[SerializeField] private for prefab references and configuration — never public fields.ProjectConfig.yaml before using any API — breaking changes happen between major versions.Awaitable with CancellationToken for async network operations (connection, scene loading) per CODING_STANDARDS.md..asmdef.ProjectConfig.yaml -> mcp.unity_mcp is true, call refresh_unity after creating files.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.