claude-code-arcane 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +54 -6
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +5 -0
- package/docs/SKILLS-CATALOG.md +42 -3
- package/docs/USER-GUIDE.md +2 -2
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +1 -1
- package/profiles/unreal-dev.yaml +97 -0
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- package/templates/gamedev/unreal-project-context.md +107 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# RPC Decision Guide
|
|
2
|
+
|
|
3
|
+
Reference for choosing the correct RPC type (Server, Client, NetMulticast) and
|
|
4
|
+
reliability level (Reliable, Unreliable) in Unreal Engine multiplayer code.
|
|
5
|
+
All patterns are grounded in real `APlayerController` usage from `PlayerController.h`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## RPC Type Quick Reference
|
|
10
|
+
|
|
11
|
+
| Specifier | Called on | Executes on | Typical use |
|
|
12
|
+
|-----------------|------------|--------------------------|----------------------------------------|
|
|
13
|
+
| `Server` | Client | Server (Authority) | Player input, purchase, ability use |
|
|
14
|
+
| `Client` | Server | Owning client only | UI updates, player-specific feedback |
|
|
15
|
+
| `NetMulticast` | Server | Server + all clients | World effects visible to everyone |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Decision Flowchart
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Is this a request from a player to change game state?
|
|
23
|
+
YES --> use Server RPC (client calls, server executes)
|
|
24
|
+
NO --> continue
|
|
25
|
+
|
|
26
|
+
Should this run only on one specific player's machine?
|
|
27
|
+
YES --> use Client RPC (server calls, owning client executes)
|
|
28
|
+
NO --> continue
|
|
29
|
+
|
|
30
|
+
Should this run on the server AND every connected client simultaneously?
|
|
31
|
+
YES --> use NetMulticast RPC (server calls, all machines execute)
|
|
32
|
+
NO --> reconsider: is property replication a better fit?
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Server RPC
|
|
38
|
+
|
|
39
|
+
**Who calls it**: the client (specifically the owning client of the actor).
|
|
40
|
+
**Who executes it**: the server.
|
|
41
|
+
**Primary use**: forwarding player intent to the authoritative machine.
|
|
42
|
+
|
|
43
|
+
All Server RPCs that change game state must include `WithValidation`. This generates
|
|
44
|
+
a `_Validate` function the engine calls before `_Implementation`. Returning `false`
|
|
45
|
+
from `_Validate` kicks the calling client.
|
|
46
|
+
|
|
47
|
+
```cpp
|
|
48
|
+
// Declaration
|
|
49
|
+
UFUNCTION(Server, Reliable, WithValidation)
|
|
50
|
+
void ServerFireWeapon(FVector_NetQuantize MuzzleLocation,
|
|
51
|
+
FVector_NetQuantizeNormal Direction);
|
|
52
|
+
|
|
53
|
+
// Implementation (.cpp)
|
|
54
|
+
void AMyCharacter::ServerFireWeapon_Implementation(
|
|
55
|
+
FVector_NetQuantize MuzzleLocation,
|
|
56
|
+
FVector_NetQuantizeNormal Direction)
|
|
57
|
+
{
|
|
58
|
+
// Server performs authoritative hit trace
|
|
59
|
+
FHitResult Hit;
|
|
60
|
+
FCollisionQueryParams Params;
|
|
61
|
+
Params.AddIgnoredActor(this);
|
|
62
|
+
|
|
63
|
+
if (GetWorld()->LineTraceSingleByChannel(
|
|
64
|
+
Hit, MuzzleLocation, MuzzleLocation + Direction * 10000.f,
|
|
65
|
+
ECC_Pawn, Params))
|
|
66
|
+
{
|
|
67
|
+
if (AMyCharacter* Victim = Cast<AMyCharacter>(Hit.GetActor()))
|
|
68
|
+
{
|
|
69
|
+
Victim->ApplyDamage_Authority(WeaponDamage);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Spawn bullet tracer effect on all clients
|
|
74
|
+
MulticastSpawnTracer(MuzzleLocation, Hit.ImpactPoint);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
bool AMyCharacter::ServerFireWeapon_Validate(
|
|
78
|
+
FVector_NetQuantize MuzzleLocation,
|
|
79
|
+
FVector_NetQuantizeNormal Direction)
|
|
80
|
+
{
|
|
81
|
+
// Reject zero directions
|
|
82
|
+
if (Direction.IsNearlyZero()) return false;
|
|
83
|
+
// Reject muzzle locations impossibly far from actor
|
|
84
|
+
if (FVector::Dist(MuzzleLocation, GetActorLocation()) > 500.f) return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Real examples from `PlayerController.h`:
|
|
90
|
+
|
|
91
|
+
```cpp
|
|
92
|
+
// Player acknowledges pawn possession — must not be lost, Reliable
|
|
93
|
+
UFUNCTION(reliable, server, WithValidation)
|
|
94
|
+
void ServerAcknowledgePossession(class APawn* P);
|
|
95
|
+
|
|
96
|
+
// Spectator position — sent frequently, OK to drop, Unreliable
|
|
97
|
+
UFUNCTION(unreliable, server, WithValidation)
|
|
98
|
+
void ServerSetSpectatorLocation(FVector NewLoc, FRotator NewRot);
|
|
99
|
+
|
|
100
|
+
// Server restart — must not be dropped, Reliable
|
|
101
|
+
UFUNCTION(reliable, server, WithValidation)
|
|
102
|
+
void ServerRestartPlayer();
|
|
103
|
+
|
|
104
|
+
// Camera update — sent frequently, can be dropped, Unreliable
|
|
105
|
+
UFUNCTION(unreliable, server, WithValidation)
|
|
106
|
+
void ServerUpdateCamera(FVector_NetQuantize CamLoc, int32 CamPitchAndYaw);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### When to use Reliable vs Unreliable for Server RPCs
|
|
110
|
+
|
|
111
|
+
| Scenario | Reliability |
|
|
112
|
+
|-----------------------------------|--------------|
|
|
113
|
+
| Purchase, ability activation | Reliable |
|
|
114
|
+
| Possession / respawn | Reliable |
|
|
115
|
+
| Player name or setting change | Reliable |
|
|
116
|
+
| Level streaming notification | Reliable |
|
|
117
|
+
| Frequent position / camera update | Unreliable |
|
|
118
|
+
| High-frequency input relay | Unreliable |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Client RPC
|
|
123
|
+
|
|
124
|
+
**Who calls it**: the server.
|
|
125
|
+
**Who executes it**: the owning client of the actor.
|
|
126
|
+
**Primary use**: sending player-specific information or triggering private feedback.
|
|
127
|
+
|
|
128
|
+
`Client` RPCs do not need `WithValidation` — the server already has authority.
|
|
129
|
+
|
|
130
|
+
```cpp
|
|
131
|
+
// Declaration
|
|
132
|
+
UFUNCTION(Client, Reliable)
|
|
133
|
+
void ClientShowMatchResult(bool bWon, int32 FinalScore);
|
|
134
|
+
|
|
135
|
+
// Implementation
|
|
136
|
+
void AMyPlayerController::ClientShowMatchResult_Implementation(
|
|
137
|
+
bool bWon, int32 FinalScore)
|
|
138
|
+
{
|
|
139
|
+
// Runs only on the owning client — safe to access local UI
|
|
140
|
+
if (UMyHUD* HUD = Cast<UMyHUD>(GetHUD()))
|
|
141
|
+
{
|
|
142
|
+
HUD->ShowMatchResult(bWon, FinalScore);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Real examples from `PlayerController.h`:
|
|
148
|
+
|
|
149
|
+
```cpp
|
|
150
|
+
// Tell the client it's been muted — Reliable, player must know
|
|
151
|
+
UFUNCTION(Reliable, Client)
|
|
152
|
+
void ClientMutePlayer(FUniqueNetIdRepl PlayerId);
|
|
153
|
+
|
|
154
|
+
// Send a localized message to the owning client — Reliable
|
|
155
|
+
UFUNCTION(Reliable, Client)
|
|
156
|
+
void ClientReceiveLocalizedMessage(TSubclassOf<ULocalMessage> Message,
|
|
157
|
+
int32 Switch,
|
|
158
|
+
APlayerState* RelatedPlayerState_1,
|
|
159
|
+
APlayerState* RelatedPlayerState_2,
|
|
160
|
+
UObject* OptionalObject);
|
|
161
|
+
|
|
162
|
+
// Client spectator waiting state — Reliable, state change must not be lost
|
|
163
|
+
UFUNCTION(client, reliable)
|
|
164
|
+
void ClientSetSpectatorWaiting(bool bWaiting);
|
|
165
|
+
|
|
166
|
+
// Async physics timestamp sync — Reliable, setup data
|
|
167
|
+
UFUNCTION(Client, Reliable)
|
|
168
|
+
void ClientSetupNetworkPhysicsTimestamp(FAsyncPhysicsTimestamp Timestamp);
|
|
169
|
+
|
|
170
|
+
// Time dilation update — Unreliable, continuously corrected
|
|
171
|
+
UFUNCTION(Client, Unreliable)
|
|
172
|
+
void ClientAckTimeDilation(float TimeDilation, int32 ServerStep);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### When to use Reliable vs Unreliable for Client RPCs
|
|
176
|
+
|
|
177
|
+
| Scenario | Reliability |
|
|
178
|
+
|--------------------------------------------|--------------|
|
|
179
|
+
| UI state change (match result, kill feed) | Reliable |
|
|
180
|
+
| Player kicked / game over notification | Reliable |
|
|
181
|
+
| View target / camera set | Reliable |
|
|
182
|
+
| Sound / haptic triggered by server event | Reliable |
|
|
183
|
+
| Camera time dilation correction | Unreliable |
|
|
184
|
+
| High-frequency positional feedback effect | Unreliable |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## NetMulticast RPC
|
|
189
|
+
|
|
190
|
+
**Who calls it**: the server.
|
|
191
|
+
**Who executes it**: the server AND all clients that have this actor replicated to them.
|
|
192
|
+
**Primary use**: cosmetic world events that everyone should see.
|
|
193
|
+
|
|
194
|
+
```cpp
|
|
195
|
+
// Declaration
|
|
196
|
+
UFUNCTION(NetMulticast, Unreliable)
|
|
197
|
+
void MulticastPlayExplosionEffect(FVector Location, float Radius);
|
|
198
|
+
|
|
199
|
+
// Implementation
|
|
200
|
+
void AMyActor::MulticastPlayExplosionEffect_Implementation(
|
|
201
|
+
FVector Location, float Radius)
|
|
202
|
+
{
|
|
203
|
+
// Runs on server and all clients — guard so server doesn't spawn visual effects
|
|
204
|
+
if (!HasAuthority())
|
|
205
|
+
{
|
|
206
|
+
UGameplayStatics::SpawnEmitterAtLocation(
|
|
207
|
+
GetWorld(), ExplosionFX, Location);
|
|
208
|
+
UGameplayStatics::PlaySoundAtLocation(
|
|
209
|
+
GetWorld(), ExplosionSound, Location);
|
|
210
|
+
}
|
|
211
|
+
// Both server and client: apply physics impulse to overlapping actors
|
|
212
|
+
ApplyExplosionImpulse(Location, Radius);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Multicast vs Property Replication
|
|
217
|
+
|
|
218
|
+
Prefer multicast for **one-shot events** (sounds, VFX, transient animations).
|
|
219
|
+
Prefer **property replication** for **persistent state** (health, flags, positions).
|
|
220
|
+
|
|
221
|
+
| Data type | Mechanism |
|
|
222
|
+
|------------------------------------|----------------------------------|
|
|
223
|
+
| Plays once, no recovery needed | NetMulticast (Unreliable) |
|
|
224
|
+
| Plays once, must not be missed | NetMulticast (Reliable) |
|
|
225
|
+
| Persists on clients that join late | UPROPERTY(Replicated) |
|
|
226
|
+
| Different value per client | Client RPC |
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Reliable vs Unreliable — Definitive Rules
|
|
231
|
+
|
|
232
|
+
### Use Reliable when
|
|
233
|
+
|
|
234
|
+
- The action has **permanent game state consequences** (purchase, death, spawn).
|
|
235
|
+
- The receiver must **acknowledge** receipt to proceed (possession, level load).
|
|
236
|
+
- The data is **not redundant** — there is no future update that carries the same info.
|
|
237
|
+
- Dropping the packet would cause a **visible or gameplay-breaking desync**.
|
|
238
|
+
|
|
239
|
+
### Use Unreliable when
|
|
240
|
+
|
|
241
|
+
- The data is **superseded frequently** by the next tick's update (position, rotation).
|
|
242
|
+
- It is purely **cosmetic** (hit sparks, footstep dust, screenshake).
|
|
243
|
+
- Dropping it produces only a minor visual glitch, not a gameplay error.
|
|
244
|
+
- The RPC is called **very often** (every tick or multiple times per second) — using
|
|
245
|
+
Reliable for high-frequency calls saturates the reliable channel and causes lag spikes.
|
|
246
|
+
|
|
247
|
+
### Reliable Channel Saturation Warning
|
|
248
|
+
|
|
249
|
+
The reliable channel is a FIFO queue. If too many reliable messages queue up (e.g.,
|
|
250
|
+
a Server Reliable RPC called every frame), the connection will stall waiting for
|
|
251
|
+
acknowledgments. This manifests as rubberbanding and eventually a timeout disconnect.
|
|
252
|
+
|
|
253
|
+
**Rule**: never call a Reliable RPC more than once per server tick per connection.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Ownership Requirement for RPCs
|
|
258
|
+
|
|
259
|
+
RPCs will silently fail (not execute, not error) if the actor does not have a valid
|
|
260
|
+
network connection through its ownership chain.
|
|
261
|
+
|
|
262
|
+
### Server RPC routing
|
|
263
|
+
|
|
264
|
+
The calling client must own the actor in the chain:
|
|
265
|
+
`APlayerController -> APawn -> AWeapon`
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
Client calls WeaponActor->ServerFire()
|
|
269
|
+
-> Engine looks up WeaponActor->GetOwner() -> APawn
|
|
270
|
+
-> APawn->GetOwner() -> APlayerController
|
|
271
|
+
-> APlayerController->NetConnection == the calling client connection
|
|
272
|
+
-> RPC is routed correctly
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
If `WeaponActor->GetOwner()` is `nullptr` or another player's controller, the RPC
|
|
276
|
+
will be dropped on the client. Fix: call `SetOwner(PlayerController)` on the server
|
|
277
|
+
after spawning the weapon.
|
|
278
|
+
|
|
279
|
+
### Client RPC routing
|
|
280
|
+
|
|
281
|
+
The server must own the actor and it must be associated with a specific player's
|
|
282
|
+
`NetConnection`. This is why most Client RPCs are called on `APlayerController`
|
|
283
|
+
directly:
|
|
284
|
+
|
|
285
|
+
```cpp
|
|
286
|
+
// Server code — send a message to a specific player
|
|
287
|
+
APlayerController* PC = GetPlayerController(PlayerIndex);
|
|
288
|
+
PC->ClientReceiveLocalizedMessage(Message, Switch, nullptr, nullptr, nullptr);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Calling a Client RPC on an actor with no owning player connection will silently fail.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Common RPC Mistakes
|
|
296
|
+
|
|
297
|
+
### Calling a Server RPC from the Server
|
|
298
|
+
|
|
299
|
+
A Server RPC called from the server's own code executes locally — the engine
|
|
300
|
+
skips routing. This is not harmful but is misleading and wastes a function call.
|
|
301
|
+
Prefer calling the `_Implementation` function directly from server code if you need
|
|
302
|
+
to invoke the logic without network routing.
|
|
303
|
+
|
|
304
|
+
```cpp
|
|
305
|
+
// MISLEADING — on the server, this just calls Implementation directly
|
|
306
|
+
ServerDoThing();
|
|
307
|
+
|
|
308
|
+
// CLEAR — explicitly call the implementation when you know you're on the server
|
|
309
|
+
if (HasAuthority())
|
|
310
|
+
{
|
|
311
|
+
ServerDoThing_Implementation(Params);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Calling a Client RPC from the Client
|
|
316
|
+
|
|
317
|
+
A Client RPC called from the client is silently ignored — it does not route up to
|
|
318
|
+
the server and back down. If you need to trigger something on the server that then
|
|
319
|
+
responds to the client, use a Server RPC + Client RPC pair.
|
|
320
|
+
|
|
321
|
+
### Using NetMulticast Instead of Property Replication for State
|
|
322
|
+
|
|
323
|
+
Multicast RPCs are not replayed for clients that join after the call. If a new player
|
|
324
|
+
joins after an explosion, they will not see the aftermath if it was communicated only
|
|
325
|
+
via multicast. Use replicated properties for persistent state.
|
|
326
|
+
|
|
327
|
+
```cpp
|
|
328
|
+
// WRONG — late-joining clients miss this
|
|
329
|
+
MulticastSetDoorOpen(true);
|
|
330
|
+
|
|
331
|
+
// CORRECT — new clients see the correct state on join
|
|
332
|
+
bDoorOpen = true; // replicated property — MARK_PROPERTY_DIRTY if using Iris
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Missing WithValidation on Server RPCs That Modify State
|
|
336
|
+
|
|
337
|
+
Any Server RPC that changes game state (inventory, health, abilities) without
|
|
338
|
+
validation is a security hole. Malicious clients can send arbitrary parameter values.
|
|
339
|
+
|
|
340
|
+
```cpp
|
|
341
|
+
// INSECURE
|
|
342
|
+
UFUNCTION(Server, Reliable)
|
|
343
|
+
void ServerAddItem(int32 ItemId, int32 Quantity); // client can claim any quantity
|
|
344
|
+
|
|
345
|
+
// SECURE
|
|
346
|
+
UFUNCTION(Server, Reliable, WithValidation)
|
|
347
|
+
void ServerRequestPurchase(int32 ItemId); // server looks up price from data tables
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### RPC Parameters Containing Non-Replicated Object Pointers
|
|
351
|
+
|
|
352
|
+
Object pointers in RPC parameters must be net-addressable. Passing a pointer to a
|
|
353
|
+
locally spawned, non-replicated UObject will result in a `nullptr` on the remote side.
|
|
354
|
+
|
|
355
|
+
```cpp
|
|
356
|
+
// WRONG — LocalEffect is not replicated; remote gets nullptr
|
|
357
|
+
ServerDoThingWithEffect(LocalEffect);
|
|
358
|
+
|
|
359
|
+
// CORRECT — pass a data class reference or an ID, look it up on the remote side
|
|
360
|
+
UFUNCTION(Server, Reliable, WithValidation)
|
|
361
|
+
void ServerActivateEffectById(int32 EffectDataId);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## RPC vs Property Replication Decision
|
|
367
|
+
|
|
368
|
+
| Situation | Preferred mechanism |
|
|
369
|
+
|----------------------------------------------------|--------------------------------------------|
|
|
370
|
+
| Value changes on server, clients just read it | `UPROPERTY(Replicated)` |
|
|
371
|
+
| Value changes, client needs custom callback logic | `UPROPERTY(ReplicatedUsing = OnRep_Func)` |
|
|
372
|
+
| One-shot event, all clients react simultaneously | `NetMulticast` RPC |
|
|
373
|
+
| One-shot event, only one specific client reacts | `Client` RPC |
|
|
374
|
+
| Client requests server to do something | `Server` RPC |
|
|
375
|
+
| Ordering matters AND property replication is lossy | `Client` RPC (as noted in `PlayerController.h` comments) |
|
|
376
|
+
| High-frequency cosmetic (dust, sparks) | `NetMulticast Unreliable` RPC |
|
|
377
|
+
| Late-join clients must see current state | `UPROPERTY(Replicated)` always |
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## RPC Implementation Naming Convention
|
|
382
|
+
|
|
383
|
+
UE generates the implementation and validation function names automatically from the
|
|
384
|
+
declared function name:
|
|
385
|
+
|
|
386
|
+
| Declared name | Generated names |
|
|
387
|
+
|-------------------------|----------------------------------------------------|
|
|
388
|
+
| `ServerFireWeapon` | `ServerFireWeapon_Implementation`, `ServerFireWeapon_Validate` |
|
|
389
|
+
| `ClientShowResult` | `ClientShowResult_Implementation` |
|
|
390
|
+
| `MulticastPlayEffect` | `MulticastPlayEffect_Implementation` |
|
|
391
|
+
|
|
392
|
+
The `UFUNCTION` macro declaration uses the base name. The body you write is always
|
|
393
|
+
`_Implementation`. Only Server RPCs with `WithValidation` also need `_Validate`.
|