claude-code-arcane 2.3.0 → 2.5.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +131 -41
  3. package/agents/game/ue-blueprint-specialist.md +125 -0
  4. package/agents/game/ue-gas-specialist.md +133 -0
  5. package/agents/game/ue-umg-specialist.md +131 -0
  6. package/dist/cli.js +642 -478
  7. package/docs/SKILLS-CATALOG.md +42 -3
  8. package/docs/USER-GUIDE.md +28 -54
  9. package/hooks/detect-unreal.sh +90 -0
  10. package/package.json +2 -1
  11. package/profiles/agile.yaml +1 -1
  12. package/profiles/ai.yaml +1 -1
  13. package/profiles/android-native.yaml +1 -1
  14. package/profiles/audio.yaml +1 -1
  15. package/profiles/backend-dotnet.yaml +1 -1
  16. package/profiles/backend-go.yaml +1 -1
  17. package/profiles/backend-nestjs.yaml +1 -1
  18. package/profiles/backend-nextjs.yaml +1 -1
  19. package/profiles/backend-ts.yaml +1 -1
  20. package/profiles/business.yaml +1 -1
  21. package/profiles/clevel.yaml +1 -1
  22. package/profiles/clickup.yaml +1 -1
  23. package/profiles/core.yaml +1 -1
  24. package/profiles/database.yaml +1 -1
  25. package/profiles/design.yaml +1 -1
  26. package/profiles/docs.yaml +1 -1
  27. package/profiles/ecommerce.yaml +1 -1
  28. package/profiles/finance.yaml +1 -1
  29. package/profiles/flutter.yaml +1 -1
  30. package/profiles/freelance.yaml +1 -1
  31. package/profiles/frontend.yaml +1 -1
  32. package/profiles/infra.yaml +1 -1
  33. package/profiles/integrations.yaml +1 -1
  34. package/profiles/ios-native.yaml +1 -1
  35. package/profiles/jira.yaml +1 -1
  36. package/profiles/job-hunt.yaml +1 -1
  37. package/profiles/marketing.yaml +1 -1
  38. package/profiles/mobile.yaml +1 -1
  39. package/profiles/regulatory.yaml +1 -1
  40. package/profiles/second-brain.yaml +1 -1
  41. package/profiles/security.yaml +1 -1
  42. package/profiles/self-improving.yaml +1 -1
  43. package/profiles/statusline.yaml +1 -1
  44. package/profiles/testing.yaml +1 -1
  45. package/profiles/unity-design.yaml +1 -1
  46. package/profiles/unity-dev.yaml +1 -1
  47. package/profiles/unreal-dev.yaml +97 -0
  48. package/profiles/visual-novel.yaml +1 -1
  49. package/rules/gamedev/unreal-code.md +125 -0
  50. package/skills/arcane-list/SKILL.md +12 -7
  51. package/skills/install-mcp/SKILL.md +135 -46
  52. package/skills/install-mcp/references/unreal-operations.md +55 -0
  53. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  54. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  55. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  56. package/skills/ue-ai-navigation/SKILL.md +526 -0
  57. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  58. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  59. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  60. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  61. package/skills/ue-animation-system/SKILL.md +521 -0
  62. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  63. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  64. package/skills/ue-async-threading/SKILL.md +433 -0
  65. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  66. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  67. package/skills/ue-audio-system/SKILL.md +527 -0
  68. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  69. package/skills/ue-blueprints/SKILL.md +134 -0
  70. package/skills/ue-blueprints/references/communication.md +82 -0
  71. package/skills/ue-character-movement/SKILL.md +475 -0
  72. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  73. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  74. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  75. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  76. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  77. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  78. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  79. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  80. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  81. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  82. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  83. package/skills/ue-editor-tools/SKILL.md +523 -0
  84. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  85. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  86. package/skills/ue-game-features/SKILL.md +477 -0
  87. package/skills/ue-game-features/references/experience-system.md +222 -0
  88. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  89. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  90. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  91. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  92. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  93. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  94. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  95. package/skills/ue-input-system/SKILL.md +394 -0
  96. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  97. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  98. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  99. package/skills/ue-mass-entity/SKILL.md +476 -0
  100. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  101. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  102. package/skills/ue-materials-rendering/SKILL.md +526 -0
  103. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  104. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  105. package/skills/ue-module-build-system/SKILL.md +487 -0
  106. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  107. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  108. package/skills/ue-naming-conventions/SKILL.md +142 -0
  109. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  110. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  111. package/skills/ue-networking-replication/SKILL.md +452 -0
  112. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  113. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  114. package/skills/ue-niagara-effects/SKILL.md +531 -0
  115. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  116. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  117. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  118. package/skills/ue-packaging/SKILL.md +116 -0
  119. package/skills/ue-physics-collision/SKILL.md +423 -0
  120. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  121. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  122. package/skills/ue-procedural-generation/SKILL.md +505 -0
  123. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  124. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  125. package/skills/ue-project-context/SKILL.md +305 -0
  126. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  127. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  128. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  129. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  130. package/skills/ue-state-trees/SKILL.md +489 -0
  131. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  132. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  133. package/skills/ue-testing-debugging/SKILL.md +520 -0
  134. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  135. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  136. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  137. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  138. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  139. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  140. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  141. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,526 @@
1
+ # Replication Patterns
2
+
3
+ Common patterns for replicating game state in Unreal Engine multiplayer projects.
4
+ Each pattern includes the header declarations, implementation, and client callback.
5
+
6
+ ---
7
+
8
+ ## Pattern 1: Health with OnRep Callback
9
+
10
+ The most common pattern. Health is authoritative on the server. Clients receive the
11
+ updated value and react via `OnRep_Health` (update HUD, play hurt effects).
12
+
13
+ ```cpp
14
+ // AMyCharacter.h
15
+ UPROPERTY(ReplicatedUsing = OnRep_Health)
16
+ float Health;
17
+
18
+ UPROPERTY(ReplicatedUsing = OnRep_MaxHealth)
19
+ float MaxHealth;
20
+
21
+ UFUNCTION()
22
+ void OnRep_Health(float PreviousHealth);
23
+
24
+ UFUNCTION()
25
+ void OnRep_MaxHealth(float PreviousMaxHealth);
26
+
27
+ // Server RPC — clients request damage (usually called by server game logic instead)
28
+ UFUNCTION(Server, Reliable, WithValidation)
29
+ void ServerApplyHealing(float Amount);
30
+ ```
31
+
32
+ ```cpp
33
+ // AMyCharacter.cpp
34
+ #include "Net/UnrealNetwork.h"
35
+
36
+ void AMyCharacter::GetLifetimeReplicatedProps(
37
+ TArray<FLifetimeProperty>& OutLifetimeProps) const
38
+ {
39
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
40
+ DOREPLIFETIME(AMyCharacter, Health);
41
+ DOREPLIFETIME(AMyCharacter, MaxHealth);
42
+ }
43
+
44
+ void AMyCharacter::OnRep_Health(float PreviousHealth)
45
+ {
46
+ // Called on clients only — update UI and effects
47
+ OnHealthChanged.Broadcast(PreviousHealth, Health);
48
+ if (Health < PreviousHealth)
49
+ {
50
+ PlayHurtMontage();
51
+ }
52
+ }
53
+
54
+ void AMyCharacter::OnRep_MaxHealth(float PreviousMaxHealth)
55
+ {
56
+ OnMaxHealthChanged.Broadcast(PreviousMaxHealth, MaxHealth);
57
+ }
58
+
59
+ // Called by game logic on server only
60
+ void AMyCharacter::ApplyDamage_Authority(float Damage)
61
+ {
62
+ if (!HasAuthority()) return;
63
+ Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth);
64
+ if (Health <= 0.f)
65
+ {
66
+ Die_Authority();
67
+ }
68
+ }
69
+
70
+ void AMyCharacter::ServerApplyHealing_Implementation(float Amount)
71
+ {
72
+ // Validate amount is positive and actor is alive
73
+ Health = FMath::Clamp(Health + Amount, 0.f, MaxHealth);
74
+ }
75
+
76
+ bool AMyCharacter::ServerApplyHealing_Validate(float Amount)
77
+ {
78
+ return Amount > 0.f && Amount <= 500.f; // reject absurd values
79
+ }
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Pattern 2: Team Data (Owner-Only Private + All-Clients Public)
85
+
86
+ Split information based on who needs it. Score is public; private currency is owner-only.
87
+
88
+ ```cpp
89
+ // AMyPlayerState.h (PlayerState replicates to all clients)
90
+ UPROPERTY(ReplicatedUsing = OnRep_TeamId)
91
+ int32 TeamId;
92
+
93
+ UPROPERTY(Replicated)
94
+ int32 Score;
95
+
96
+ UPROPERTY(Replicated)
97
+ int32 Kills;
98
+
99
+ // Private to owning player only
100
+ UPROPERTY(ReplicatedUsing = OnRep_Currency)
101
+ int32 Currency;
102
+ ```
103
+
104
+ ```cpp
105
+ // AMyPlayerState.cpp
106
+ void AMyPlayerState::GetLifetimeReplicatedProps(
107
+ TArray<FLifetimeProperty>& OutLifetimeProps) const
108
+ {
109
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
110
+
111
+ DOREPLIFETIME(AMyPlayerState, TeamId);
112
+ DOREPLIFETIME(AMyPlayerState, Score);
113
+ DOREPLIFETIME(AMyPlayerState, Kills);
114
+
115
+ // Only the owning client receives their own currency
116
+ DOREPLIFETIME_CONDITION(AMyPlayerState, Currency, COND_OwnerOnly);
117
+ }
118
+
119
+ void AMyPlayerState::OnRep_TeamId()
120
+ {
121
+ // Update team color, nameplate, etc. on all clients
122
+ OnTeamChanged.Broadcast(TeamId);
123
+ }
124
+
125
+ void AMyPlayerState::OnRep_Currency()
126
+ {
127
+ // Update shop UI — only fires on the owning client
128
+ OnCurrencyChanged.Broadcast(Currency);
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Pattern 3: Inventory Using FFastArraySerializer
135
+
136
+ Efficient replicated inventory — only changed items are sent over the network,
137
+ not the entire array.
138
+
139
+ ```cpp
140
+ // InventoryTypes.h
141
+ USTRUCT(BlueprintType)
142
+ struct FInventoryItem : public FFastArraySerializerItem
143
+ {
144
+ GENERATED_BODY()
145
+
146
+ UPROPERTY()
147
+ int32 ItemId = 0;
148
+
149
+ UPROPERTY()
150
+ int32 Quantity = 0;
151
+
152
+ UPROPERTY()
153
+ float Durability = 1.0f;
154
+
155
+ void PreReplicatedRemove(const struct FInventoryList& InArraySerializer);
156
+ void PostReplicatedAdd(const struct FInventoryList& InArraySerializer);
157
+ void PostReplicatedChange(const struct FInventoryList& InArraySerializer);
158
+ };
159
+
160
+ USTRUCT()
161
+ struct FInventoryList : public FFastArraySerializer
162
+ {
163
+ GENERATED_BODY()
164
+
165
+ UPROPERTY()
166
+ TArray<FInventoryItem> Items;
167
+
168
+ bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms)
169
+ {
170
+ return FFastArraySerializer::FastArrayDeltaSerialize<FInventoryItem, FInventoryList>(
171
+ Items, DeltaParms, *this);
172
+ }
173
+ };
174
+
175
+ template<>
176
+ struct TStructOpsTypeTraits<FInventoryList>
177
+ : public TStructOpsTypeTraitsBase2<FInventoryList>
178
+ {
179
+ enum { WithNetDeltaSerializer = true };
180
+ };
181
+ ```
182
+
183
+ ```cpp
184
+ // UMyInventoryComponent.h
185
+ UCLASS()
186
+ class UMyInventoryComponent : public UActorComponent
187
+ {
188
+ GENERATED_BODY()
189
+
190
+ public:
191
+ virtual void GetLifetimeReplicatedProps(
192
+ TArray<FLifetimeProperty>& OutLifetimeProps) const override;
193
+
194
+ // Only the owning player sees their own inventory
195
+ UPROPERTY(ReplicatedUsing = OnRep_Inventory)
196
+ FInventoryList Inventory;
197
+
198
+ UFUNCTION()
199
+ void OnRep_Inventory();
200
+
201
+ // Server-side mutation
202
+ void AddItem_Authority(int32 ItemId, int32 Quantity);
203
+ void RemoveItem_Authority(int32 ItemId, int32 Quantity);
204
+ };
205
+ ```
206
+
207
+ ```cpp
208
+ // UMyInventoryComponent.cpp
209
+ void UMyInventoryComponent::GetLifetimeReplicatedProps(
210
+ TArray<FLifetimeProperty>& OutLifetimeProps) const
211
+ {
212
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
213
+ DOREPLIFETIME_CONDITION(UMyInventoryComponent, Inventory, COND_OwnerOnly);
214
+ }
215
+
216
+ void UMyInventoryComponent::AddItem_Authority(int32 ItemId, int32 Quantity)
217
+ {
218
+ if (!GetOwner()->HasAuthority()) return;
219
+
220
+ // Find existing stack
221
+ for (FInventoryItem& Item : Inventory.Items)
222
+ {
223
+ if (Item.ItemId == ItemId)
224
+ {
225
+ Item.Quantity += Quantity;
226
+ Inventory.MarkItemDirty(Item); // tell FastArray this item changed
227
+ return;
228
+ }
229
+ }
230
+
231
+ // Add new stack
232
+ FInventoryItem& NewItem = Inventory.Items.AddDefaulted_GetRef();
233
+ NewItem.ItemId = ItemId;
234
+ NewItem.Quantity = Quantity;
235
+ Inventory.MarkItemDirty(NewItem);
236
+ }
237
+
238
+ // Callbacks on client
239
+ void FInventoryItem::PostReplicatedAdd(const FInventoryList& InArraySerializer)
240
+ {
241
+ // New item appeared in inventory — update UI
242
+ }
243
+
244
+ void FInventoryItem::PostReplicatedChange(const FInventoryList& InArraySerializer)
245
+ {
246
+ // Existing item quantity or durability changed — update slot UI
247
+ }
248
+
249
+ void FInventoryItem::PreReplicatedRemove(const FInventoryList& InArraySerializer)
250
+ {
251
+ // Item is about to be removed — clear UI slot
252
+ }
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Pattern 4: Ability State (InitialOnly + Runtime Flags)
258
+
259
+ Ability unlocks are set once; cooldowns and charges update dynamically.
260
+
261
+ ```cpp
262
+ // UMyAbilityComponent.h
263
+ // Which abilities are unlocked — sent once at spawn, never changes
264
+ UPROPERTY(Replicated)
265
+ TArray<int32> UnlockedAbilityIds;
266
+
267
+ // Current cooldown end times per ability slot
268
+ UPROPERTY(ReplicatedUsing = OnRep_Cooldowns)
269
+ TArray<float> CooldownEndTimes;
270
+
271
+ // Active ability flags bitmask
272
+ UPROPERTY(ReplicatedUsing = OnRep_ActiveFlags)
273
+ uint8 ActiveAbilityFlags;
274
+
275
+ UFUNCTION()
276
+ void OnRep_Cooldowns();
277
+
278
+ UFUNCTION()
279
+ void OnRep_ActiveFlags(uint8 PreviousFlags);
280
+ ```
281
+
282
+ ```cpp
283
+ // UMyAbilityComponent.cpp
284
+ void UMyAbilityComponent::GetLifetimeReplicatedProps(
285
+ TArray<FLifetimeProperty>& OutLifetimeProps) const
286
+ {
287
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
288
+
289
+ // Unlocked abilities: all clients see which abilities a player has
290
+ DOREPLIFETIME_CONDITION(UMyAbilityComponent, UnlockedAbilityIds, COND_InitialOnly);
291
+
292
+ // Cooldowns: only the owning client needs exact cooldown timers
293
+ DOREPLIFETIME_CONDITION(UMyAbilityComponent, CooldownEndTimes, COND_OwnerOnly);
294
+
295
+ // Active flags: all clients see which abilities are currently active (for animation)
296
+ DOREPLIFETIME(UMyAbilityComponent, ActiveAbilityFlags);
297
+ }
298
+
299
+ void UMyAbilityComponent::OnRep_Cooldowns()
300
+ {
301
+ // Refresh cooldown bar UI for owning player only
302
+ }
303
+
304
+ void UMyAbilityComponent::OnRep_ActiveFlags(uint8 PreviousFlags)
305
+ {
306
+ // Determine which bits changed and trigger corresponding animations/effects
307
+ uint8 ChangedBits = ActiveAbilityFlags ^ PreviousFlags;
308
+ for (int32 i = 0; i < 8; ++i)
309
+ {
310
+ if (ChangedBits & (1 << i))
311
+ {
312
+ bool bNowActive = (ActiveAbilityFlags & (1 << i)) != 0;
313
+ OnAbilityActivationChanged.Broadcast(i, bNowActive);
314
+ }
315
+ }
316
+ }
317
+ ```
318
+
319
+ ---
320
+
321
+ ## Pattern 5: Replicated Object Subobject (UE 5.1+ API)
322
+
323
+ A `UObject` subobject with its own replicated properties, registered via the modern API.
324
+
325
+ ```cpp
326
+ // UMyQuestData.h
327
+ UCLASS()
328
+ class UMyQuestData : public UObject
329
+ {
330
+ GENERATED_BODY()
331
+
332
+ public:
333
+ virtual void GetLifetimeReplicatedProps(
334
+ TArray<FLifetimeProperty>& OutLifetimeProps) const override;
335
+
336
+ virtual bool IsSupportedForNetworking() const override { return true; }
337
+
338
+ UPROPERTY(Replicated)
339
+ int32 QuestId;
340
+
341
+ UPROPERTY(Replicated)
342
+ int32 Progress;
343
+
344
+ UPROPERTY(Replicated)
345
+ bool bCompleted;
346
+ };
347
+ ```
348
+
349
+ ```cpp
350
+ // AMyPlayerController.cpp — register the subobject during BeginPlay on server
351
+ void AMyPlayerController::BeginPlay()
352
+ {
353
+ Super::BeginPlay();
354
+ if (HasAuthority())
355
+ {
356
+ ActiveQuest = NewObject<UMyQuestData>(this);
357
+ ActiveQuest->QuestId = 42;
358
+ // Register with COND_OwnerOnly so only this player's client receives it
359
+ AddReplicatedSubObject(ActiveQuest, COND_OwnerOnly);
360
+ }
361
+ }
362
+
363
+ void AMyPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
364
+ {
365
+ if (HasAuthority() && ActiveQuest)
366
+ {
367
+ RemoveReplicatedSubObject(ActiveQuest);
368
+ }
369
+ Super::EndPlay(EndPlayReason);
370
+ }
371
+ ```
372
+
373
+ ```cpp
374
+ // UMyQuestData.cpp
375
+ void UMyQuestData::GetLifetimeReplicatedProps(
376
+ TArray<FLifetimeProperty>& OutLifetimeProps) const
377
+ {
378
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
379
+ DOREPLIFETIME(UMyQuestData, QuestId);
380
+ DOREPLIFETIME(UMyQuestData, Progress);
381
+ DOREPLIFETIME(UMyQuestData, bCompleted);
382
+ }
383
+ ```
384
+
385
+ ---
386
+
387
+ ## Pattern 6: Replicated Actor Movement (FRepMovement)
388
+
389
+ For actors that move but do not use `UCharacterMovementComponent`, replicate movement
390
+ via `bReplicateMovement` and the built-in `FRepMovement` struct.
391
+
392
+ ```cpp
393
+ // AMyVehicle.h
394
+ AMyVehicle()
395
+ {
396
+ bReplicates = true;
397
+ SetReplicateMovement(true); // enables FRepMovement replication via OnRep_ReplicatedMovement
398
+ SetNetUpdateFrequency(50.f);
399
+ NetPriority = 2.5f;
400
+ }
401
+
402
+ // Override to apply custom interpolation when movement data arrives on simulated proxies
403
+ virtual void OnRep_ReplicatedMovement() override;
404
+ ```
405
+
406
+ ```cpp
407
+ // AMyVehicle.cpp
408
+ void AMyVehicle::OnRep_ReplicatedMovement()
409
+ {
410
+ Super::OnRep_ReplicatedMovement(); // applies FRepMovement to root component
411
+
412
+ // Additional logic: apply physics state, snap wheels, etc.
413
+ SyncWheelPositions();
414
+ }
415
+ ```
416
+
417
+ For physics-simulated actors, set `bRepPhysics = true` on the `FRepMovement` to
418
+ include linear and angular velocity, allowing clients to simulate physics correctly.
419
+
420
+ ---
421
+
422
+ ## Pattern 7: Custom Relevancy Override
423
+
424
+ Override `IsNetRelevantFor` to implement game-specific relevancy (team-based, zone-based, etc.).
425
+
426
+ ```cpp
427
+ // AMyActor.h
428
+ virtual bool IsNetRelevantFor(
429
+ const AActor* RealViewer,
430
+ const AActor* ViewTarget,
431
+ const FVector& SrcLocation) const override;
432
+ ```
433
+
434
+ ```cpp
435
+ // AMyActor.cpp — only replicate to players on the same team
436
+ bool AMyActor::IsNetRelevantFor(
437
+ const AActor* RealViewer,
438
+ const AActor* ViewTarget,
439
+ const FVector& SrcLocation) const
440
+ {
441
+ // Always relevant to owner
442
+ if (RealViewer == GetOwner()) return true;
443
+
444
+ // Check team
445
+ const AMyPlayerController* PC = Cast<AMyPlayerController>(RealViewer);
446
+ if (PC && PC->GetTeamId() == TeamId)
447
+ {
448
+ // Also respect distance — call default distance check
449
+ return Super::IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
450
+ }
451
+
452
+ return false;
453
+ }
454
+ ```
455
+
456
+ ---
457
+
458
+ ## Pattern 8: Dormancy for Rarely-Updated Actors
459
+
460
+ Actors like pickups and map objectives update infrequently. Use dormancy to skip
461
+ replication checks when nothing changes.
462
+
463
+ ```cpp
464
+ // AMyPickup.h
465
+ AMyPickup()
466
+ {
467
+ bReplicates = true;
468
+ NetDormancy = DORM_DormantAll; // start dormant
469
+ SetNetUpdateFrequency(1.f);
470
+ }
471
+
472
+ void PickupCollected(ACharacter* Collector);
473
+ ```
474
+
475
+ ```cpp
476
+ // AMyPickup.cpp
477
+ void AMyPickup::PickupCollected(ACharacter* Collector)
478
+ {
479
+ if (!HasAuthority()) return;
480
+
481
+ bPickedUp = true;
482
+ SetActorHiddenInGame(true);
483
+ SetActorEnableCollision(false);
484
+
485
+ // Wake up for one replication update to push bPickedUp to clients, then re-dormant
486
+ FlushNetDormancy();
487
+
488
+ // Schedule respawn
489
+ GetWorldTimerManager().SetTimer(
490
+ RespawnTimer, this, &AMyPickup::Respawn_Authority, RespawnTime, false);
491
+ }
492
+
493
+ void AMyPickup::Respawn_Authority()
494
+ {
495
+ bPickedUp = false;
496
+ SetActorHiddenInGame(false);
497
+ SetActorEnableCollision(true);
498
+ FlushNetDormancy(); // push respawn state, then return to dormant
499
+ }
500
+ ```
501
+
502
+ ---
503
+
504
+ ## Bandwidth Estimation Guide
505
+
506
+ | Data Type | Approximate Bits per Update |
507
+ |---------------------------------|-----------------------------|
508
+ | `bool` (replicated) | 1 bit + overhead (~8 bits) |
509
+ | `uint8` | 8 bits |
510
+ | `int32` | 32 bits |
511
+ | `float` | 32 bits |
512
+ | `FVector` (full precision) | 96 bits |
513
+ | `FVector_NetQuantize` | ~30-48 bits |
514
+ | `FVector_NetQuantize10` | ~30-48 bits (1/10 cm) |
515
+ | `FRotator` (full) | 96 bits |
516
+ | `FRotator` compressed | ~24 bits |
517
+ | `FRepMovement` | ~100-200 bits |
518
+ | RPC call overhead | ~80-120 bits + params |
519
+
520
+ Use `FVector_NetQuantize` variants for world positions and directions to halve
521
+ bandwidth compared to raw `FVector`. The precision tiers are:
522
+
523
+ - `FVector_NetQuantize` — 1 cm precision
524
+ - `FVector_NetQuantize10` — 0.1 cm precision
525
+ - `FVector_NetQuantize100` — 0.01 cm precision
526
+ - `FVector_NetQuantizeNormal` — unit vector, ~16 bits