Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AIGroupSerializer.c
Go to the documentation of this file.
2{
3 [Attribute("60.0", desc: "Maximum time after group creation a player has time to be reconnected to automatically join back into it.")]
4 protected float m_fMaxPlayerReconnectTime;
5
6 //------------------------------------------------------------------------------------------------
7 override static typename GetTargetType()
8 {
9 return SCR_AIGroup;
10 }
11
12 //------------------------------------------------------------------------------------------------
13 override ESerializeResult SerializeSpawnData(notnull IEntity entity, notnull SaveContext context, SerializerDefaultSpawnData defaultData)
14 {
15 context.WriteValue("version", 1);
16 context.WriteValue("prefab", SCR_ResourceNameUtils.GetPrefabName(entity));
17 return ESerializeResult.DEFAULT;
18 }
19
20 //------------------------------------------------------------------------------------------------
21 override protected ESerializeResult Serialize(notnull IEntity entity, notnull SaveContext context)
22 {
23 const SCR_AIGroup group = SCR_AIGroup.Cast(entity);
24
25 // Skip commander group that is managed by commander system in conflict
26 const SCR_EGroupRole role = group.GetGroupRole();
28 if (role == SCR_EGroupRole.COMMANDER && campaign)
29 return ESerializeResult.DEFAULT;
30
31 const bool useCommanding = SCR_CommandingManagerComponent.GetInstance() != null;
32 const UUID commandingGroup = GetSystem().GetId(group.GetMaster());
33
34 array<UUID> aiMembers();
35 array<AIAgent> outAgents();
36 group.GetAgents(outAgents);
37 foreach (auto agent : outAgents)
38 {
39 const IEntity character = agent.GetControlledEntity();
40 if (EntityUtils.IsPlayer(character))
41 continue; // Players are stored via player controller so we can decide if and how they join on reconnect.
42
43 const UUID uuid = GetSystem().GetId(character);
44 if (!uuid.IsNull())
45 aiMembers.Insert(uuid);
46 }
47
48 set<UUID> waypoints();
49 array<AIWaypoint> outWaypoints();
50 group.GetWaypoints(outWaypoints);
51 foreach (auto waypoint : outWaypoints)
52 {
53 const UUID uuid = GetSystem().GetId(waypoint);
54 if (!uuid.IsNull())
55 waypoints.Insert(uuid);
56 }
57
58 FactionKey factionKey;
59 auto faction = SCR_Faction.Cast(group.GetFaction());
60 if (faction)
61 factionKey = faction.GetFactionKey();
62
63 const bool playable = group.IsPlayable();
64 const ResourceName preset = group.GetPresetResource();
65 array<int> playerIds = group.GetPlayerIDs();
66
67 // If entirely empty usually not needed unless commander setup some groups in advance
68 if (aiMembers.IsEmpty() && playerIds.IsEmpty() && !group.IsCreatedByCommander())
69 return ESerializeResult.DEFAULT;
70
71 context.WriteValue("version", 1);
72 context.Write(factionKey);
73
74 if (useCommanding)
75 context.WriteDefault(commandingGroup, UUID.NULL_UUID);
76
77 if (!aiMembers.IsEmpty() || !context.CanSeekMembers())
78 context.Write(aiMembers);
79
80 if (!waypoints.IsEmpty() || !context.CanSeekMembers())
81 context.Write(waypoints);
82
83 if (playable)
84 {
85 const int groupId = group.GetGroupID();
86 const int radioFrequency = group.GetRadioFrequency();
87 const string flag = group.GetGroupFlag();
88 const bool isPrivate = group.IsPrivate();
89 const bool isPrivateChangeable = group.IsPrivacyChangeable();
90 const string customName = group.GetCustomName();
91 const string customDesc = group.GetCustomDescription();
92 const int maxMembers = group.GetMaxMembers();
93 const SCR_ECharacterRank requiredRank = group.GetRequiredRank();
94 const bool deleteIfNoPlayer = group.GetDeleteIfNoPlayer();
95
96 UUID rallyBase = UUID.NULL_UUID;
97 const bool rallyForced = group.IsRallyPointForced();
98 if (campaign)
99 {
100 const SCR_CampaignMilitaryBaseManager baseManager = campaign.GetBaseManager();
101 if (baseManager)
102 {
104 if (base)
105 rallyBase = GetSystem().GetId(base.GetOwner());
106 }
107 }
108
109 context.Write(radioFrequency);
110 context.WriteDefault(preset, string.Empty);
111 if (preset)
112 context.Write(groupId);
113
114 context.WriteDefault(role, SCR_EGroupRole.NONE);
115 context.WriteDefault(flag, string.Empty);
116 context.WriteDefault(isPrivate, false);
117 context.WriteDefault(isPrivateChangeable, false);
118 context.WriteDefault(customName, string.Empty);
119 context.WriteDefault(customDesc, string.Empty);
120 context.WriteDefault(maxMembers, 0);
121 context.WriteDefault(requiredRank, SCR_ECharacterRank.PRIVATE);
122 context.WriteDefault(deleteIfNoPlayer, true);
123 context.WriteDefault(rallyBase, UUID.NULL_UUID);
124 if (!rallyBase.IsNull())
125 context.WriteDefault(rallyForced, false);
126
127 // Ensure the leader id is at idx 0
128 const int leaderId = group.GetLeaderID();
129 if (playerIds.RemoveItem(leaderId))
130 playerIds.InsertAt(leaderId, 0);
131
132 array<UUID> playerIdentities();
133 foreach (auto playerId : playerIds)
134 {
135 const UUID playerIdentity = SCR_PlayerIdentityUtils.GetPlayerIdentityId(playerId);
136 if (!playerIdentity.IsNull())
137 playerIdentities.Insert(playerIdentity);
138 }
139
140 if (!playerIdentities.IsEmpty() || !context.CanSeekMembers())
141 context.Write(playerIdentities);
142 }
143
144 return ESerializeResult.OK;
145 }
146
147 //------------------------------------------------------------------------------------------------
148 override bool DeserializeSpawnData(out ResourceName prefab, notnull out EntitySpawnParams params, notnull LoadContext context)
149 {
150 int version;
151 context.Read(version);
152 context.ReadValue("prefab", prefab);
153
154 // Avoid group prefab spawning child units automatically, persistence logic will handle it.
156 return true;
157 }
158
159 //------------------------------------------------------------------------------------------------
160 override protected bool Deserialize(notnull IEntity entity, notnull LoadContext context)
161 {
162 // Allow group spawning again
164
165 auto group = SCR_AIGroup.Cast(entity);
166 group.ActivateAI();
167
168 int version;
169 context.Read(version);
170
171 FactionKey factionKey;
172 context.Read(factionKey);
173 Faction faction = GetGame().GetFactionManager().GetFactionByKey(factionKey);
174 if (faction)
175 group.SetFaction(faction);
176
177 UUID commandingGroup = UUID.NULL_UUID;
178 auto commanding = SCR_CommandingManagerComponent.GetInstance();
179 if (commanding)
180 {
181 context.ReadDefault(commandingGroup, UUID.NULL_UUID);
182 if (!commandingGroup.IsNull())
183 {
184 Tuple1<SCR_AIGroup> commandingContext(group);
185 PersistenceWhenAvailableTask commandingTask(OnCommandingAvailable, commandingContext);
186 GetSystem().WhenAvailable(commandingGroup, commandingTask);
187 }
188 }
189
190 // Join back all aiMembers
191 array<UUID> aiMembers();
192 context.Read(aiMembers);
193 foreach (int idx, auto member : aiMembers)
194 {
195 Tuple2<SCR_AIGroup, bool> memberContext(group, idx == 0); //Leader at idx 0 of aiMembers
196 PersistenceWhenAvailableTask memberTask(OnAiMemberAvailable, memberContext);
197 GetSystem().WhenAvailable(member, memberTask);
198 }
199
200 // Reconnect to all waypoints in the right order. For now we simply unlink all waypoints
201 array<AIWaypoint> outWaypoints();
202 group.GetWaypoints(outWaypoints);
203 foreach (auto waypoint : outWaypoints)
204 {
205 group.RemoveWaypoint(waypoint);
206 }
207
208 array<UUID> waypoints();
209 context.Read(waypoints);
210 foreach (int idx, auto waypoint : waypoints)
211 {
212 Tuple2<SCR_AIGroup, int> waypointContext(group, idx);
213 PersistenceWhenAvailableTask waypointTask(OnWaypointAvailable, waypointContext);
214 GetSystem().WhenAvailable(waypoint, waypointTask);
215 }
216
217 if (group.IsPlayable())
218 {
219 int radioFrequency;
220 context.Read(radioFrequency);
221 group.SetRadioFrequency(radioFrequency);
222
223 ResourceName preset;
224 if (context.Read(preset))
225 {
226 group.SetPresetResource(preset);
227 if (preset)
228 {
229 int groupId;
230 context.Read(groupId);
231 group.SetGroupID(groupId);
232 }
233 }
234
235 SCR_EGroupRole role;
236 if (context.Read(role))
237 group.SetGroupRole(role);
238
239 string flag;
240 context.ReadDefault(flag, string.Empty);
241 if (!flag.IsEmpty())
242 {
243 group.SetCustomGroupFlag(flag);
244 if (!flag.StartsWith("{"))
245 group.SetFlagIsFromImageSet(true);
246 }
247
248 bool isPrivate;
249 if (context.Read(isPrivate))
250 group.SetPrivate(isPrivate);
251
252 bool isPrivateChangeable;
253 if (context.Read(isPrivateChangeable))
254 group.SetPrivacyChangeable(isPrivateChangeable);
255
256 string customName;
257 context.ReadDefault(customName, string.Empty);
258
259 string customDesc;
260 context.ReadDefault(customDesc, string.Empty);
261
262 int maxMembers;
263 if (context.Read(maxMembers))
264 group.SetMaxGroupMembers(maxMembers);
265
266 SCR_ECharacterRank requiredRank;
267 if (context.Read(requiredRank))
268 group.SetRequiredRank(requiredRank);
269
270 bool deleteIfNoPlayer;
271 context.ReadDefault(deleteIfNoPlayer, true);
272 group.SetCanDeleteIfNoPlayer(deleteIfNoPlayer);
273
274 UUID rallyBase;
275 context.ReadDefault(rallyBase, UUID.NULL_UUID);
276 if (!rallyBase.IsNull())
277 {
278 bool rallyForced;
279 context.ReadDefault(rallyForced, false);
280
281 Tuple2<SCR_AIGroup, bool> rallyContext(group, rallyForced);
282 PersistenceWhenAvailableTask rallyTask(OnRallyBaseAvailable, rallyContext);
283 GetSystem().WhenAvailable(rallyBase, rallyTask);
284 }
285
286 auto groupsManager = SCR_GroupsManagerComponent.GetInstance();
287 if (groupsManager)
288 {
289 groupsManager.AssignGroupID(group);
290 groupsManager.RegisterGroup(group);
291 groupsManager.ClaimFrequency(radioFrequency, faction);
292 groupsManager.OnGroupCreated(group);
293 }
294
295 array<UUID> playerIdentities();
296 context.Read(playerIdentities);
297 foreach (int idx, auto playerIdentity : playerIdentities)
298 {
299 Tuple4<SCR_AIGroup, bool, string, string> playerContext(group, idx == 0, string.Empty, string.Empty); // Player Identity 0 is from the leader
300 if (playerContext.param2)
301 {
302 // Name and desc are only re-applied if the leader joins back as per UGC requirements
303 playerContext.param3 = customName;
304 playerContext.param4 = customDesc;
305 }
306
307 PersistenceWhenAvailableTask AddPlayerIdTask(OnPlayerMemberAvailable, playerContext);
308 GetSystem().WhenAvailable(playerIdentity, AddPlayerIdTask, m_fMaxPlayerReconnectTime);
309 }
310 }
311
312 return true;
313 }
314
315 //------------------------------------------------------------------------------------------------
316 protected static void OnPlayerMemberAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
317 {
318 auto playerController = SCR_PlayerController.Cast(instance);
319 if (!playerController)
320 return;
321
322 auto playerContext = Tuple4<SCR_AIGroup, bool, string, string>.Cast(context);
323 if (!playerContext.param1)
324 return; // Group no longer exists
325
326 auto playerGroupController = SCR_PlayerControllerGroupComponent.Cast(playerController.FindComponent(SCR_PlayerControllerGroupComponent));
327 if (!playerGroupController)
328 return;
329
330 const int groupId = playerContext.param1.GetGroupID();
331 playerGroupController.RPC_AskJoinGroup(groupId);
332 if (playerGroupController.GetGroupID() != groupId)
333 return; // Failed to join group - e.g. was full already.
334
335 if (playerContext.param2 || playerContext.param1.GetLeaderID() == -1)
336 {
337 const int playerId = playerController.GetPlayerId();
338 playerContext.param1.SetGroupLeader(playerId);
339
340 if (!playerContext.param3.IsEmpty())
341 playerContext.param1.SetCustomName(playerContext.param3, playerId);
342
343 if (!playerContext.param4.IsEmpty())
344 playerContext.param1.SetCustomDescription(playerContext.param4, playerId);
345 }
346 }
347
348 //------------------------------------------------------------------------------------------------
349 protected static void OnAiMemberAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
350 {
351 auto member = SCR_ChimeraCharacter.Cast(instance);
352 if (!member)
353 return;
354
355 AIAgent aiAgent = SCR_AIUtils.GetAIAgent(member);
356 if (!aiAgent)
357 return;
358
359 aiAgent.ActivateAI();
360
361 auto groupContext = Tuple2<SCR_AIGroup, bool>.Cast(context);
362 if (!groupContext.param1)
363 return; // Group gone
364
365 groupContext.param1.AddAgent(aiAgent);
366
367 if (groupContext.param2)
368 groupContext.param1.SetNewLeader(aiAgent);
369
370 // Commanding
371 if (groupContext.param1.GetMaster())
372 {
373 auto aiMembers = groupContext.param1.GetAIMembers();
374 if (!aiMembers.Contains(member))
375 {
376 aiMembers.Insert(member);
377 member.SetRecruited(true);
378 }
379 }
380 }
381
382 //------------------------------------------------------------------------------------------------
383 protected static void OnWaypointAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
384 {
385 auto waypoint = AIWaypoint.Cast(instance);
386 if (!waypoint)
387 return;
388
389 auto waypointContext = Tuple2<SCR_AIGroup, int>.Cast(context);
390 if (waypointContext.param1)
391 waypointContext.param1.AddWaypointAt(waypoint, waypointContext.param2);
392 }
393
394 //------------------------------------------------------------------------------------------------
395 protected static void OnCommandingAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
396 {
397 auto masterGroup = SCR_AIGroup.Cast(instance);
398 if (!masterGroup)
399 return;
400
401 auto commandingContext = Tuple1<SCR_AIGroup>.Cast(context);
402 if (!commandingContext.param1)
403 return;
404
405 auto masterRpl = RplComponent.Cast(masterGroup.FindComponent(RplComponent));
406 auto slaveRpl = RplComponent.Cast(commandingContext.param1.FindComponent(RplComponent));
407 if (!masterRpl || !slaveRpl)
408 return;
409
410 auto groupsManager = SCR_GroupsManagerComponent.GetInstance();
411 groupsManager.RequestSetGroupSlave(masterRpl.Id(), slaveRpl.Id());
412
413 // Go through all members of the slave group and ensure they are added to the scripted array and marked as recruited.
414 // If a member is loaded after this setup the individual member available callback will recheck and add them.
415 array<AIAgent> outAgents();
416 commandingContext.param1.GetAgents(outAgents);
417 auto aiMembers = commandingContext.param1.GetAIMembers();
418 foreach (auto agent : outAgents)
419 {
420 auto member = SCR_ChimeraCharacter.Cast(agent.GetControlledEntity());
421 if (!aiMembers.Contains(member))
422 {
423 aiMembers.Insert(member);
424 member.SetRecruited(true);
425 }
426 }
427 }
428
429 //------------------------------------------------------------------------------------------------
430 protected static void OnRallyBaseAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
431 {
432 auto owner = IEntity.Cast(instance);
433 if (!owner)
434 return;
435
437 if (!base)
438 return;
439
440 auto rallyContext = Tuple2<SCR_AIGroup, bool>.Cast(context);
441 if (rallyContext.param1)
442 rallyContext.param1.SetRallyPoint(base, rallyContext.param2);
443 }
444}
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_BaseGameMode GetGameMode()
void SCR_CommandingManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
SCR_EGroupRole
Group roles.
void SCR_GameModeCampaign(IEntitySource src, IEntity parent)
void SCR_GroupsManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
SCR_EGroupRole GetGroupRole()
SCR_ECharacterRank GetRequiredRank()
array< int > GetPlayerIDs()
ResourceName GetPresetResource()
string GetCustomName()
int GetRallyPointId()
bool IsPrivacyChangeable()
bool IsRallyPointForced()
int GetRadioFrequency()
int GetMaxMembers()
static void IgnoreSpawning(bool ignore)
bool IsPrivate()
SCR_AIGroup GetMaster()
int GetGroupID()
string GetCustomDescription()
bool IsCreatedByCommander()
int GetLeaderID()
Faction GetFaction()
bool GetDeleteIfNoPlayer()
Returns true if the group is set to be deleted when all players leave.
bool IsPlayable()
ResourceName GetGroupFlag()
static override GetTargetType()
override bool DeserializeSpawnData(out ResourceName prefab, notnull out EntitySpawnParams params, notnull LoadContext context)
static void OnWaypointAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
static void OnPlayerMemberAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
override ESerializeResult SerializeSpawnData(notnull IEntity entity, notnull SaveContext context, SerializerDefaultSpawnData defaultData)
ESerializeResult Serialize(notnull IEntity entity, notnull SaveContext context)
static void OnRallyBaseAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
static void OnAiMemberAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
bool Deserialize(notnull IEntity entity, notnull LoadContext context)
static void OnCommandingAvailable(Managed instance, PersistenceDeferredDeserializeTask task, bool expired, Managed context)
SCR_CampaignMilitaryBaseComponent FindBaseByCallsign(int callsign)
Collection of data to avoid writing known defaults during serialization.
Definition UUID.c:28
void EntitySpawnParams()
Definition gameLib.c:130
SCR_FieldOfViewSettings Attribute
ESerializeResult
void Tuple1(T1 p1)
Definition tuple.c:37