Lesson 10: Cheat and Abuse Surface Triage - Server Validation and Rate Limits
Most early multiplayer slices fail security in the same way: they trust client intent too much.
The goal for this lesson is not to build enterprise anti-cheat. It is to stop the highest-impact abuse patterns before your first external playtest.
Lesson objective
By the end of this lesson, you will have:
- A small cheat-surface inventory for your current slice
- Server-side validation for high-risk gameplay actions
- Basic per-action rate limits to block spam abuse
- A triage rubric for go/no-go playtest decisions
Why this matters now
You already have headless smoke infrastructure from Lesson 9.
Now you need to ensure the server does not accept impossible state changes from modified clients or replay spam.
Fixing these issues early prevents:
- progression corruption during playtests
- unfair matches that invalidate feedback
- noisy bug reports caused by obvious exploit paths
Step-by-step triage pass
Step 1 - Build an abuse surface list
List every client-originating action that mutates important state:
- damage apply
- item pickup or currency grant
- objective completion signal
- movement mode toggles
- match flow actions (ready, start, vote, restart)
Mark each action by impact:
Critical: breaks match integrityHigh: gives major unfair advantageMedium: annoys or destabilizes sessionLow: cosmetic or low-impact misuse
Prioritize only Critical and High in this lesson.
Step 2 - Enforce authority checks on server handlers
For each critical action, verify:
- caller identity is known and authenticated in-session
- caller is allowed to perform this action
- target state transition is legal in current game phase
Never trust client-side prechecks as final authority.
Step 3 - Validate payload boundaries
Before applying server mutations, validate payloads:
- numeric bounds (damage, speed, stack size)
- enum allow-lists (action type, ability IDs)
- object ownership (requested target belongs to expected context)
- timing constraints (cannot trigger before cooldown or phase unlock)
Reject invalid payloads and log compact reason codes for analysis.
Step 4 - Add lightweight action rate limiting
Implement a simple per-client token bucket or cooldown map for abuse-prone actions:
- fire/use ability RPCs
- inventory transfer RPCs
- session flow requests
You are not trying to perfectly classify intent. You are preventing abuse bursts that can melt slice stability.
Step 5 - Define enforcement outcomes
Use clear outcomes for violations:
warn: first suspicious eventdrop_action: invalid payload or exceeded rateflag_session: repeated abuse patternkick_client: severe repeated integrity failures
Keep this policy explicit so QA and design understand enforcement behavior during tests.
Example - server-side action guard
using Unity.Netcode;
using UnityEngine;
public class CombatActionGuard : NetworkBehaviour
{
[ServerRpc(RequireOwnership = false)]
public void RequestAttackServerRpc(int abilityId, float claimedDamage, ServerRpcParams rpcParams = default)
{
ulong sender = rpcParams.Receive.SenderClientId;
if (!IsValidAbility(abilityId))
{
Debug.LogWarning($"ANTIABUSE drop_action reason=invalid_ability sender={sender}");
return;
}
if (claimedDamage < 0f || claimedDamage > 250f)
{
Debug.LogWarning($"ANTIABUSE drop_action reason=damage_out_of_range sender={sender}");
return;
}
if (!CanActNow(sender, abilityId))
{
Debug.LogWarning($"ANTIABUSE drop_action reason=rate_limited sender={sender}");
return;
}
ApplyAuthoritativeAttack(sender, abilityId, claimedDamage);
}
private bool IsValidAbility(int abilityId) => abilityId >= 0 && abilityId <= 12;
private bool CanActNow(ulong sender, int abilityId) { return true; }
private void ApplyAuthoritativeAttack(ulong sender, int abilityId, float claimedDamage) { }
}
This is intentionally lightweight. Expand later with replay detection and deeper behavioral telemetry.
Mini challenge
Create an abuse-triage.md sheet with:
- five client-originating actions
- impact rating for each
- current validation status (
missing,partial,done) - one concrete mitigation per missing item
Then implement two highest-risk mitigations before your next playtest build.
Pro tips
- Keep anti-abuse logs compact and structured for quick filtering.
- Attach a
build_idto enforcement logs so regression tracking is easier. - Prefer deterministic rejection over hidden correction for debugging clarity.
Common mistakes
- Validating on client but not on server.
- Trusting claimed values (damage, distance, currency) without server recompute or bounds checks.
- Adding aggressive kick logic before observability exists.
Troubleshooting
"Legit players get rate-limited during normal combat."
Increase burst allowance or separate limits by action class so core gameplay is unaffected.
"Cheat reports are vague and hard to reproduce."
Add reason codes and sender IDs in logs, then compare against session timeline.
"Server validation causes desync complaints."
Ensure client prediction displays pending state and reconciles gracefully on rejection.
FAQ
Do I need third-party anti-cheat now?
No. For a vertical slice, strong server validation and sane rate limits provide most of the immediate value.
Should we instantly ban abusive clients in playtests?
Usually no. Start with logging + action drops, then escalate once patterns are confirmed.
How often should triage be rerun?
Every time new networked gameplay actions are added or modified.
Recap
You now have a practical anti-abuse baseline: identify high-impact surfaces, validate on server, and rate-limit spam-prone actions.
Next lesson teaser
Lesson 11 moves into profiling with networking counters so you can correlate bandwidth, tick behavior, and GC spikes with real multiplayer sessions.
Related links
- Lesson 9: Server Build and Headless Test
- Unity Netcode Documentation
- Unity Guide - Common Unity Errors and Fast Fixes
Run this triage before every friends-and-family drop. Fairness and stability are core to trustworthy multiplayer feedback.