OpenClonk
C4PlayerInfo.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2004-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 // permanent player information management
18 // see header for some additional information
19 
20 #include "C4Include.h"
21 #include "control/C4PlayerInfo.h"
22 
23 #include "c4group/C4Components.h"
24 #include "control/C4GameControl.h"
25 #include "game/C4FullScreen.h"
26 #include "player/C4Player.h"
27 #include "player/C4PlayerList.h"
28 
29 // *** C4PlayerInfo
30 
32 {
33  // del temp file
35  // clear fields
36  sName.Clear(); szFilename.Clear();
37  pRes = nullptr;
38  ResCore.Clear();
39  // default fields
40  dwColor = dwOriginalColor = 0xffffffff;
41  dwAlternateColor = 0;
42  dwFlags = 0;
43  iID = idSavegamePlayer = idTeam = 0;
44  iInGameNumber = iInGameJoinFrame = iInGamePartFrame = -1;
45  sLeagueAccount = ""; iLeagueScore=iLeagueRank=0;
46  iLeagueProjectedGain = -1;
47  eType = C4PT_User;
48  idExtraData = C4ID::None;
49  iLeaguePerformance = 0;
50  sLeagueProgressData.Clear();
51 }
52 
54 {
55  // is temp file?
56  if (!! szFilename && (dwFlags & PIF_TempFile))
57  {
58  // erase it
59  EraseItem(szFilename.getData());
60  // reset flag and filename to prevent double deletion
61  dwFlags &= ~PIF_TempFile;
62  szFilename.Clear();
63  }
64 }
65 
66 bool C4PlayerInfo::LoadFromLocalFile(const char *szFilename)
67 {
68  // players should not be added in replay mode
69  assert(!Game.C4S.Head.Replay);
70  // clear previous
71  Clear();
72  // open player file group
73  C4Group Grp;
74  if (!Reloc.Open(Grp, szFilename)) return false;
75 
76  // read core
77  C4PlayerInfoCore C4P;
78  if (!C4P.Load(Grp)) return false;
79  // set values
80  eType = C4PT_User;
81  sName = C4P.PrefName;
82  this->szFilename = szFilename;
83  dwColor = dwOriginalColor = 0xff000000 | (C4P.PrefColorDw & 0xffffff); // ignore alpha
84  dwAlternateColor = 0xff000000 | (C4P.PrefColor2Dw & 0xffffff); // ignore alpha
85  // network: resource (not for replays, because everyone has the player files there...)
86  if (::Network.isEnabled() && !Game.C4S.Head.Replay)
87  {
88  // add resource
89  // 2do: rejoining players need to update their resource version when saving the player
90  // otherwise, player file versions may differ
91  pRes = ::Network.ResList.getRefRes(szFilename, true);
92  // not found? add
93  if (!pRes) pRes = ::Network.ResList.AddByGroup(&Grp, false, NRT_Player, -1, szFilename);
94  if (!pRes) return false;
95  // set core and flag
96  ResCore = pRes->getCore();
97  dwFlags |= PIF_HasRes;
98  // filename is no longer needed in network mode, because it's stored in the res-core
99  }
100  // done, success
101  return true;
102 }
103 
104 bool C4PlayerInfo::SetAsScriptPlayer(const char *szName, uint32_t dwColor, uint32_t dwFlags, C4ID idExtra)
105 {
106  // clear previous
107  Clear();
108  // set parameters
109  eType = C4PT_Script;
110  this->dwColor = dwOriginalColor = 0xff000000 | (dwColor & 0xffffff); // ignore alpha
111  dwAlternateColor = 0;
112  this->sName.CopyValidated(szName);
113  idExtraData = idExtra;
114  this->dwFlags |= dwFlags;
115  // done, success
116  return true;
117 }
118 
120 {
121  // preferred: by resource
122  if (pRes) return pRes->getFile();
123  // if no resource is known (replay or non-net), return filename
124  return szFilename.getData();
125 }
126 
128 {
129  // special case if random teams and team colors are enabled in lobby:
130  // Unjoined players do not show their team! Instead, they just display their original color
132  if (Game.Teams.IsTeamColors())
133  if (Game.Teams.GetTeamByID(GetTeam()))
135  return GetOriginalColor();
136  // otherwise, just show the normal player color
137  return GetColor();
138 }
139 
141 {
142  // return player name including colored clan/team tag if known
143  StdStrBuf sResult;
144  if (sLeagueAccount.getLength())
145  {
146  if (sClanTag.getLength())
147  {
148  // gray team tag color used in lobby and game evaluation dialog!
149  sResult.Format("<c afafaf>%s</c> %s", sClanTag.getData(), sLeagueAccount.getData());
150  }
151  else
152  sResult.Ref(sLeagueAccount);
153  }
154  else
155  {
156  // fallback to regular player name
157  sResult.Ref(sForcedName.getLength() ? static_cast<const StdStrBuf &>(sForcedName) : static_cast<const StdStrBuf &>(sName));
158  }
159  return sResult;
160 }
161 
163 {
164  // team win/solo win
165  C4Team *pTeam;
166  if (idTeam && (pTeam = Game.Teams.GetTeamByID(idTeam)))
167  return pTeam->HasWon();
168  else
169  return HasWon();
170 }
171 
173 {
174  // Names
175  pComp->Value(mkNamingAdapt(sName, "Name", ""));
176  pComp->Value(mkNamingAdapt(sForcedName, "ForcedName", ""));
177  pComp->Value(mkNamingAdapt(szFilename, "Filename", ""));
178 
179  // Flags
180  const StdBitfieldEntry<uint16_t> Entries[] =
181  {
182  { "Joined", PIF_Joined },
183  { "Removed", PIF_Removed },
184  { "HasResource", PIF_HasRes },
185  { "JoinIssued", PIF_JoinIssued },
186  { "SavegameJoin", PIF_JoinedForSavegameOnly },
187  { "Disconnected", PIF_Disconnected },
188  { "VotedOut", PIF_VotedOut },
189  { "Won", PIF_Won },
190  { "AttributesFixed", PIF_AttributesFixed },
191  { "NoScenarioInit", PIF_NoScenarioInit },
192  { "NoScenarioSave", PIF_NoScenarioSave },
193  { "NoEliminationCheck", PIF_NoEliminationCheck },
194  { "Invisible", PIF_Invisible},
195  { nullptr, 0 },
196  };
197  uint16_t dwSyncFlags = dwFlags & PIF_SyncFlags; // do not store local flags!
198  pComp->Value(mkNamingAdapt(mkBitfieldAdapt(dwSyncFlags, Entries), "Flags", 0u));
199  if (pComp->isDeserializer()) dwFlags = dwSyncFlags;
200  pComp->Value(mkNamingAdapt(iID, "ID", 0));
201 
202  // type
203  StdEnumEntry<C4PlayerType> PlayerTypes[] =
204  {
205  { "User", C4PT_User },
206  { "Script", C4PT_Script },
207 
208  { nullptr, C4PT_User },
209  };
210  pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eType, PlayerTypes), "Type", C4PT_User));
211 
212  // safety: Do not allow invisible regular players
213  if (pComp->isDeserializer())
214  {
215  if (eType != C4PT_Script) dwFlags &= ~PIF_Invisible;
216  }
217 
218  // load colors
219  pComp->Value(mkNamingAdapt(dwColor, "Color", 0u));
220  pComp->Value(mkNamingAdapt(dwOriginalColor, "OriginalColor", dwColor));
221  // load savegame ID
222  pComp->Value(mkNamingAdapt(mkIntPackAdapt(idSavegamePlayer), "SavgamePlayer", 0));
223  // load team ID
224  pComp->Value(mkNamingAdapt(mkIntPackAdapt(idTeam), "Team", 0));
225  // load authentication ID
226  pComp->Value(mkNamingAdapt(szAuthID, "AUID", ""));
227 
228  // InGame info
229  if (dwFlags & PIF_Joined)
230  {
231  pComp->Value(mkNamingAdapt(iInGameNumber, "GameNumber", -1));
232  pComp->Value(mkNamingAdapt(iInGameJoinFrame, "GameJoinFrame", -1));
233  }
234  else
235  iInGameNumber = iInGameJoinFrame = -1;
236 
237  if (dwFlags & PIF_Removed)
238  pComp->Value(mkNamingAdapt(iInGamePartFrame, "GamePartFrame", -1));
239  else
240  iInGamePartFrame = -1;
241 
242  // script player extra data
243  pComp->Value(mkNamingAdapt(idExtraData, "ExtraData", C4ID::None));
244 
245  // load league info
246  pComp->Value(mkNamingAdapt(sLeagueAccount, "LeagueAccount", ""));
247  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iLeagueScore), "LeagueScore", 0));
248  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iLeagueRank), "LeagueRank", 0));
249  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iLeagueRankSymbol), "LeagueRankSymbol", 0));
250  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iLeagueProjectedGain), "ProjectedGain", -1));
251  pComp->Value(mkNamingAdapt(mkParAdapt(sClanTag, StdCompiler::RCT_All), "ClanTag", ""));
252  pComp->Value(mkNamingAdapt(mkIntPackAdapt(iLeaguePerformance), "LeaguePerformance", 0));
253  pComp->Value(mkNamingAdapt(sLeagueProgressData, "LeagueProgressData", ""));
254 
255  // file resource
256  if (pComp->isDeserializer() && Game.C4S.Head.Replay)
257  {
258  // Replays don't have player resources, drop the flag
259  dwFlags &= ~PIF_HasRes;
260  }
261  if (dwFlags & PIF_HasRes)
262  {
263  // ResCore
264  if (pComp->isSerializer() && pRes)
265  {
266  // ensure ResCore is up-to-date
267  ResCore = pRes->getCore();
268  }
269  pComp->Value(mkNamingAdapt(ResCore, "ResCore"));
270  }
271 
272 }
273 
274 void C4PlayerInfo::SetFilename(const char *szToFilename)
275 {
276  szFilename = szToFilename;
277 }
278 
279 void C4PlayerInfo::SetToScenarioFilename(const char *szScenFilename)
280 {
281  // kill res
282  DiscardResource();
283  // set new filename
284  SetFilename(szScenFilename);
285  // flag scenario filename
286  dwFlags |= PIF_InScenarioFile;
287 }
288 
290 {
291  // only if any resource present and not yet assigned
292  if (IsRemoved() || !(dwFlags & PIF_HasRes) || pRes) return;
293  // Ignore res if a local file is to be used
294  // the PIF_InScenarioFile is not set for startup players in initial replays,
295  // because resources are used for player joins but emulated in playback control
296  // if there will ever be resources in replay mode, this special case can be removed
297  if (Game.C4S.Head.Replay || (dwFlags & PIF_InScenarioFile))
298  dwFlags &= ~PIF_HasRes;
299  else
300  // create resource (will check if resource already exists)
301  if (!(pRes = ::Network.ResList.AddByCore(ResCore)))
302  {
303  dwFlags &= ~PIF_HasRes;
304  // add failed? invalid resource??! -- TODO: may be too large to load
305  LogF("Error: Could not add resource %d for player %s! Player file too large to load?", (int) ResCore.getID(), (const char *) GetFilename());
306  }
307 }
308 
310 {
311  // del any file resource
312  if (pRes)
313  {
314  assert(dwFlags & PIF_HasRes);
315  pRes = nullptr;
316  dwFlags &= ~PIF_HasRes;
317  }
318  else assert(~dwFlags & PIF_HasRes);
319  ResCore.Clear();
320 }
321 
323 {
324  // copy some data fields; but not the file fields, because the join method is determined by this player
325  if (!pSavegameInfo) return false;
326  iID = pSavegameInfo->GetID();
327  dwFlags = (dwFlags & ~PIF_SavegameTakeoverFlags) | (pSavegameInfo->GetFlags() & PIF_SavegameTakeoverFlags);
328  dwColor = pSavegameInfo->GetColor(); // redundant; should be done by host already
329  idTeam = pSavegameInfo->GetTeam();
330  return true;
331 }
332 
333 void C4PlayerInfo::SetJoined(int32_t iNumber)
334 {
335  // mark as joined in current frame
336  iInGameNumber = iNumber;
337  iInGameJoinFrame = Game.FrameCounter;
338  dwFlags |= PIF_Joined;
339 }
340 
342 {
343  // mark as removed - always marks as previously joined, too
344  dwFlags |= PIF_Joined | PIF_Removed;
345  // remember removal frame
346  iInGamePartFrame = Game.FrameCounter;
347 }
348 
350 {
351  bool fSuccess = false;
352  // load BigIcon.png of player into target facet; return false if no bigicon present or player file not yet loaded
353  C4Group Plr;
354  C4Network2Res *pRes = nullptr;
355  bool fIncompleteRes = false;
356  if ((pRes = GetRes()))
357  if (!pRes->isComplete())
358  fIncompleteRes = true;
359  size_t iBigIconSize=0;
360  if (!fIncompleteRes)
361  if (Plr.Open(pRes ? pRes->getFile() : GetFilename()))
362  if (Plr.AccessEntry(C4CFN_BigIcon, &iBigIconSize))
363  if (iBigIconSize<=C4NetResMaxBigicon*1024)
364  if (fctTarget.Load(Plr, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0))
365  fSuccess = true;
366  return fSuccess;
367 }
368 
369 
370 // *** C4ClientPlayerInfos
371 
372 C4ClientPlayerInfos::C4ClientPlayerInfos(const char *szJoinFilenames, bool fAdd, C4PlayerInfo *pAddInfo)
373 {
374  // init for local client?
375  if (szJoinFilenames || pAddInfo)
376  {
377  // set developer flag for developer hosts
378  if (SSearch(Config.GetRegistrationData("Type"), "Developer"))
379  dwFlags |= CIF_Developer;
380  // set local ID
381  iClientID = ::Control.ClientID();
382  // maybe control is not preinitialized
383  if (!::Control.isNetwork() && iClientID < 0) iClientID = 0;
384  // join packet or initial packet?
385  if (fAdd)
386  // packet is to be added to other players
387  dwFlags |= CIF_AddPlayers;
388  else
389  // set initial flag for first-time join packet
390  dwFlags |= CIF_Initial;
391  // join all players in list
392  if ((iPlayerCapacity = (szJoinFilenames ? SModuleCount(szJoinFilenames) : 0) + !!pAddInfo))
393  {
394  ppPlayers = new C4PlayerInfo *[iPlayerCapacity];
395  if (szJoinFilenames)
396  {
397  char szPlrFile[_MAX_PATH_LEN];
398  for (int32_t i=0; i<iPlayerCapacity; ++i)
399  if (SGetModule(szJoinFilenames, i, szPlrFile, _MAX_PATH))
400  {
401  C4PlayerInfo *pNewInfo = new C4PlayerInfo();
402  if (pNewInfo->LoadFromLocalFile(szPlrFile))
403  {
404  // player def loaded; register and count it
405  ppPlayers[iPlayerCount++] = pNewInfo;
406  }
407  else
408  {
409  // loading failure; clear info class
410  delete pNewInfo;
411  //
412  Log(FormatString(LoadResStr("IDS_ERR_LOAD_PLAYER"), szPlrFile).getData());
413  }
414  }
415  }
416  if (pAddInfo)
417  ppPlayers[iPlayerCount++] = pAddInfo;
418  }
419  }
420 }
421 
423 {
424  // copy fields
425  iClientID = rCopy.iClientID;
426  if ((iPlayerCount = rCopy.iPlayerCount))
427  {
428  // copy player infos
429  ppPlayers = new C4PlayerInfo *[iPlayerCapacity = rCopy.iPlayerCapacity];
430  int32_t i = iPlayerCount;
431  C4PlayerInfo **ppCurrPlrInfo = ppPlayers, **ppSrcPlrInfo = rCopy.ppPlayers;
432  while (i--) *ppCurrPlrInfo++ = new C4PlayerInfo(**ppSrcPlrInfo++);
433  }
434  // no players
435  else
436  {
437  ppPlayers = nullptr;
438  iPlayerCapacity = 0;
439  }
440  // misc fields
441  dwFlags = rCopy.dwFlags;
442 }
443 
445 {
446  Clear();
447  // copy fields
448  iClientID = rCopy.iClientID;
449  if ((iPlayerCount = rCopy.iPlayerCount))
450  {
451  // copy player infos
452  ppPlayers = new C4PlayerInfo *[iPlayerCapacity = rCopy.iPlayerCapacity];
453  int32_t i = iPlayerCount;
454  C4PlayerInfo **ppCurrPlrInfo = ppPlayers, **ppSrcPlrInfo = rCopy.ppPlayers;
455  while (i--) *ppCurrPlrInfo++ = new C4PlayerInfo(**ppSrcPlrInfo++);
456  }
457  // no players
458  else
459  {
460  ppPlayers = nullptr;
461  iPlayerCapacity = 0;
462  }
463  // misc fields
464  dwFlags = rCopy.dwFlags;
465  return *this;
466 }
467 
468 
470 {
471  // del player infos
472  int32_t i = iPlayerCount; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
473  while (i--) delete *ppCurrPlrInfo++;
474  // del player info vector
475  delete [] ppPlayers; ppPlayers = nullptr;
476  // reset other fields
477  iPlayerCount = iPlayerCapacity = 0;
478  iClientID=-1;
479  dwFlags = 0;
480 }
481 
483 {
484  // anything to grab?
485  if (!rFrom.iPlayerCount) return;
486  // any previous players to copy?
487  if (iPlayerCount)
488  {
489  // buffer sufficient?
490  if (iPlayerCount + rFrom.iPlayerCount > iPlayerCapacity)
491  GrowList(rFrom.iPlayerCount);
492  // merge into new buffer
493  memcpy(ppPlayers + iPlayerCount, rFrom.ppPlayers, rFrom.iPlayerCount * sizeof(C4PlayerInfo *));
494  iPlayerCount += rFrom.iPlayerCount;
495  rFrom.iPlayerCount = rFrom.iPlayerCapacity = 0;
496  delete [] rFrom.ppPlayers; rFrom.ppPlayers = nullptr;
497  }
498  else
499  {
500  // no own players: take over buffer of pFrom
501  if (ppPlayers) delete [] ppPlayers;
502  ppPlayers = rFrom.ppPlayers; rFrom.ppPlayers = nullptr;
503  iPlayerCount = rFrom.iPlayerCount; rFrom.iPlayerCount = 0;
504  iPlayerCapacity = rFrom.iPlayerCapacity; rFrom.iPlayerCapacity = 0;
505  }
506 }
507 
509 {
510  // grow list if necessary
511  if (iPlayerCount == iPlayerCapacity) GrowList(4);
512  // add info
513  ppPlayers[iPlayerCount++] = pAddInfo;
514 }
515 
517 {
518  // bounds check
519  if (iAtIndex<0 || iAtIndex>=iPlayerCount) return;
520  // del player info at index
521  delete ppPlayers[iAtIndex];
522  // move down last index (may self-assign a ptr)
523  ppPlayers[iAtIndex] = ppPlayers[--iPlayerCount];
524 }
525 
527 {
528  // check all infos; remove the one that matches
529  int32_t i = 0; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
530  while (i < iPlayerCount)
531  {
532  if ((*ppCurrPlrInfo)->GetID() == idPlr)
533  {
535  return;
536  }
537  ++ppCurrPlrInfo; ++i;
538  }
539  // none matched
540  return;
541 }
542 
543 void C4ClientPlayerInfos::GrowList(size_t iByVal)
544 {
545  // create new list (out of mem: simply returns here; info list remains in a valid state)
546  C4PlayerInfo **ppNewInfo = new C4PlayerInfo *[iPlayerCapacity += iByVal];
547  // move existing
548  if (ppPlayers)
549  {
550  memcpy(ppNewInfo, ppPlayers, iPlayerCount * sizeof(C4PlayerInfo *));
551  delete [] ppPlayers;
552  }
553  // assign new
554  ppPlayers = ppNewInfo;
555 }
556 
558 {
559  // check all players
560  int32_t iCount = 0;
561  int32_t i = iPlayerCount; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
562  while (i--) if ((*ppCurrPlrInfo++)->GetFlags() | dwFlag)
563  ++iCount;
564  // return number of matching infos
565  return iCount;
566 }
567 
569 {
570  // check range
571  if (iIndex<0 || iIndex>=iPlayerCount) return nullptr;
572  // return indexed info
573  return ppPlayers[iIndex];
574 }
575 
577 {
578  // get indexed matching info
579  for (int32_t iCheck=0; iCheck<iPlayerCount; ++iCheck)
580  {
581  C4PlayerInfo *pNfo = ppPlayers[iCheck];
582  if (pNfo->GetType() == eType)
583  if (!iIndex--)
584  return pNfo;
585  }
586  // nothing found
587  return nullptr;
588 }
589 
591 {
592  // check all infos
593  int32_t i = iPlayerCount; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
594  while (i--)
595  {
596  if ((*ppCurrPlrInfo)->GetID() == id) return *ppCurrPlrInfo;
597  ++ppCurrPlrInfo;
598  }
599  // none matched
600  return nullptr;
601 }
602 
604 {
605  int32_t i = iPlayerCount; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
606  C4Network2Res *pRes;
607  while (i--)
608  {
609  if ((pRes = (*ppCurrPlrInfo)->GetRes()))
610  if (pRes->getResID() == idResID)
611  // only if the player is actually using the resource
612  if ((*ppCurrPlrInfo)->IsUsingPlayerFile())
613  return *ppCurrPlrInfo;
614  ++ppCurrPlrInfo;
615  }
616  return nullptr;
617 }
618 
620 {
621  // check all players
622  int32_t i = iPlayerCount; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
623  while (i--) if (!(*ppCurrPlrInfo++)->HasJoined()) return true;
624  // all joined
625  return false;
626 }
627 
629 {
630  // count all players with IsJoined()
631  int32_t i = iPlayerCount; int32_t cnt=0; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
632  while (i--) if ((*ppCurrPlrInfo++)->IsJoined()) ++cnt;
633  return cnt;
634 }
635 
637 {
638  bool deserializing = pComp->isDeserializer();
639  if (deserializing) Clear();
640  pComp->Value(mkNamingAdapt(iClientID, "ID", C4ClientIDUnknown));
641 
642  // Flags
643  StdBitfieldEntry<uint32_t> Entries[] =
644  {
645  { "AddPlayers", CIF_AddPlayers },
646  { "Updated", CIF_Updated },
647  { "Initial", CIF_Initial },
648  { "Developer", CIF_Developer },
649 
650  { nullptr, 0 }
651  };
652  pComp->Value(mkNamingAdapt(mkBitfieldAdapt(dwFlags, Entries), "Flags", 0u));
653 
654  pComp->Value(mkNamingCountAdapt<int32_t>(iPlayerCount, "Player"));
655  if (iPlayerCount < 0 || iPlayerCount > C4MaxPlayer)
656  { pComp->excCorrupt("player count out of range"); return; }
657  // Grow list, if necessary
658  if (deserializing && iPlayerCount > iPlayerCapacity)
659  {
660  GrowList(iPlayerCount - iPlayerCapacity);
661  ZeroMem(ppPlayers, sizeof(*ppPlayers) * iPlayerCount);
662  }
663  // Compile
664  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppPlayers, iPlayerCount, mkPtrAdaptNoNull<C4PlayerInfo>), "Player"));
665  // Force specialization
666  mkPtrAdaptNoNull<C4PlayerInfo>(*ppPlayers);
667 }
668 
670 {
671  // load for all players
672  int32_t i = iPlayerCount; C4PlayerInfo **ppCurrPlrInfo = ppPlayers;
673  while (i--) (*ppCurrPlrInfo++)->LoadResource();
674 }
675 
676 // *** C4PlayerInfoList
677 
679  // ctor: no need to alloc mem yet
680 
681 C4PlayerInfoList::C4PlayerInfoList(const C4PlayerInfoList &rCpy) : iClientCount(rCpy.iClientCount), iClientCapacity(rCpy.iClientCapacity),
682  ppClients(nullptr), iLastPlayerID(rCpy.iLastPlayerID)
683 {
684  // copy client info vector
685  if (rCpy.ppClients)
686  {
687  ppClients = new C4ClientPlayerInfos*[iClientCapacity];
688  C4ClientPlayerInfos **ppInfo = ppClients, **ppCpy = rCpy.ppClients;
689  int32_t i = iClientCount;
690  while (i--) *ppInfo++ = new C4ClientPlayerInfos(**ppCpy++);
691  }
692 }
693 
695 {
696  Clear();
697  iClientCount = rCpy.iClientCount;
698  iClientCapacity = rCpy.iClientCapacity;
699  iLastPlayerID = rCpy.iLastPlayerID;
700  if (rCpy.ppClients)
701  {
702  ppClients = new C4ClientPlayerInfos*[iClientCapacity];
703  C4ClientPlayerInfos **ppInfo = ppClients, **ppCpy = rCpy.ppClients;
704  int32_t i = iClientCount;
705  while (i--) *ppInfo++ = new C4ClientPlayerInfos(**ppCpy++);
706  }
707  else
708  ppClients = nullptr;
709  return *this;
710 }
711 
713 {
714  // delete client infos
715  C4ClientPlayerInfos **ppInfo = ppClients; int32_t i=iClientCount;
716  while (i--) delete *ppInfo++;
717  // clear client infos
718  delete [] ppClients; ppClients = nullptr;
719  iClientCount = iClientCapacity = 0;
720  // reset player ID counter
721  iLastPlayerID = 0;
722 }
723 
724 void C4PlayerInfoList::GrowList(size_t iByVal)
725 {
726  // create new list (out of mem: simply returns here; info list remains in a valid state)
727  C4ClientPlayerInfos **ppNewInfo = new C4ClientPlayerInfos *[iClientCapacity += iByVal];
728  // move existing
729  if (ppClients)
730  {
731  memcpy(ppNewInfo, ppClients, iClientCount * sizeof(C4ClientPlayerInfos *));
732  delete [] ppClients;
733  }
734  // assign new
735  ppClients = ppNewInfo;
736 }
737 
738 bool C4PlayerInfoList::DoLocalNonNetworkPlayerJoin(const char *szPlayerFile)
739 {
740  // construct joining information
741  C4ClientPlayerInfos *pNewJoin = new C4ClientPlayerInfos(szPlayerFile, true);
742  // handle it
743  bool fSuccess = DoLocalNonNetworkPlayerInfoUpdate(pNewJoin);
744  // done
745  delete pNewJoin;
746  return fSuccess;
747 }
748 
750 {
751  // never done by clients or in replay - update will be handled via queue
752  if (!::Control.isCtrlHost()) return false;
753  // in network game, process by host. In offline game, just create control
754  bool fSucc = true;
755  if (::Control.isNetwork())
757  else
758  fSucc = DoLocalNonNetworkPlayerInfoUpdate(pUpdate);
759  return fSucc;
760 }
761 
763 {
764  // assign IDs first: Must be done early, so AssignTeams works
765  if (!AssignPlayerIDs(pUpdate))
766  {
767  return false;
768  }
769  // set standard teams
770  AssignTeams(pUpdate, true);
771  // color/name change by team or savegame assignment
772  UpdatePlayerAttributes(pUpdate, true);
773  // add through queue: This will add directly, do the record and put player joins into the queue
774  // in running mode, this call will also put the actual player joins into the queue
776  // done, success
777  return true;
778 }
779 
780 void C4PlayerInfoList::UpdatePlayerAttributes(C4ClientPlayerInfos *pForInfo, bool fResolveConflicts)
781 {
782  assert(pForInfo);
783  // update colors of all players of this packet
784  C4PlayerInfo *pInfo, *pInfo2; int32_t i=0;
785  while ((pInfo = pForInfo->GetPlayerInfo(i++)))
786  if (!pInfo->HasJoined())
787  {
788  // assign savegame colors
789  int32_t idSavegameID; bool fHasForcedColor = false; DWORD dwForceClr;
790  if ((idSavegameID = pInfo->GetAssociatedSavegamePlayerID()))
791  if ((pInfo2 = Game.RestorePlayerInfos.GetPlayerInfoByID(idSavegameID)))
792  {
793  dwForceClr = pInfo2->GetColor();
794  fHasForcedColor = true;
795  }
796  // assign team colors
797  if (!fHasForcedColor && Game.Teams.IsTeamColors())
798  {
799  C4Team *pPlrTeam = Game.Teams.GetTeamByID(pInfo->GetTeam());
800  if (pPlrTeam)
801  {
802  dwForceClr = pPlrTeam->GetColor();
803  fHasForcedColor = true;
804  }
805  }
806  // do color change
807  if (fHasForcedColor && (dwForceClr != pInfo->GetColor()))
808  {
809  pInfo->SetColor(dwForceClr);
810  pForInfo->SetUpdated();
811  }
812  // make sure colors have correct alpha (modified engines might send malformed packages of transparent colors)
813  if ((pInfo->GetColor() & 0xff000000u) != 0xff000000u)
814  {
815  pInfo->SetColor(pInfo->GetColor() | 0xff000000u);
816  pForInfo->SetUpdated();
817  }
818  }
819  if (fResolveConflicts) ResolvePlayerAttributeConflicts(pForInfo);
820 }
821 
823 {
824  // update attributes of all packets
825  int32_t iIdx=0;
826  C4ClientPlayerInfos *pForInfo;
827  while ((pForInfo = GetIndexedInfo(iIdx++))) UpdatePlayerAttributes(pForInfo, false);
828  // now resole all conflicts
830 }
831 
833 {
834  // assign player IDs to those player infos without
835  C4PlayerInfo *pPlrInfo; int32_t i=0;
836  while ((pPlrInfo = pNewClientInfo->GetPlayerInfo(i++)))
837  if (!pPlrInfo->GetID())
838  {
839  // are there still any player slots free?
840  if (GetFreePlayerSlotCount()<1)
841  {
842  // nope - then deny this join!
843  Log(FormatString(LoadResStr("IDS_MSG_TOOMANYPLAYERS"), (int)Game.Parameters.MaxPlayers).getData());
844  pNewClientInfo->RemoveIndexedInfo(--i);
845  continue;
846  }
847  // Join OK; grant an ID
848  pPlrInfo->SetID(++iLastPlayerID);
849  }
850  // return whether any join remains
851  return !!pNewClientInfo->GetPlayerCount();
852 }
853 
855 {
856  // number of free slots depends on max player setting
857  return std::max<int32_t>(Game.Parameters.MaxPlayers - GetStartupCount(), 0);
858 }
859 
860 void C4PlayerInfoList::AssignTeams(C4ClientPlayerInfos *pNewClientInfo, bool fByHost)
861 {
862  if (!Game.Teams.IsMultiTeams()) return;
863  // assign any unset teams (host/standalone only - fByHost determines whether the packet came from the host)
864  C4PlayerInfo *pPlrInfo; int32_t i=0;
865  while ((pPlrInfo = pNewClientInfo->GetPlayerInfo(i++)))
866  Game.Teams.RecheckPlayerInfoTeams(*pPlrInfo, fByHost);
867 }
868 
870 {
871  // ensure all teams specified in the list exist
872  C4ClientPlayerInfos *pPlrInfos; int32_t j=0;
873  while ((pPlrInfos = GetIndexedInfo(j++)))
874  {
875  C4PlayerInfo *pPlrInfo; int32_t i=0;
876  while ((pPlrInfo = pPlrInfos->GetPlayerInfo(i++)))
877  {
878  int32_t idTeam = pPlrInfo->GetTeam();
879  if (idTeam) Game.Teams.GetGenerateTeamByID(idTeam);
880  }
881  }
882 }
883 
885 {
886  assert(pNewClientInfo);
887  // caution: also called for RestorePlayerInfos-list
888  // host: reserve new IDs for all players
889  // client: all IDs should be assigned already by host
890  if (::Network.isHost() || !::Network.isEnabled())
891  {
892  if (!AssignPlayerIDs(pNewClientInfo) && pNewClientInfo->IsAddPacket())
893  {
894  // no players could be added (probably MaxPlayer)
895  delete pNewClientInfo;
896  return nullptr;
897  }
898  }
899  // ensure all teams specified in the list exist (this should be done for savegame teams as well)
900  C4PlayerInfo *pInfo; int32_t i=0;
901  while ((pInfo = pNewClientInfo->GetPlayerInfo(i++)))
902  {
903  int32_t idTeam = pInfo->GetTeam();
904  if (idTeam) Game.Teams.GetGenerateTeamByID(idTeam);
905  }
906  // add info for client; overwriting or appending to existing info if necessary
907  // try to find existing data of same client
908  C4ClientPlayerInfos **ppExistingInfo = GetInfoPtrByClientID(pNewClientInfo->GetClientID());
909  if (ppExistingInfo)
910  {
911  // info exists: append to it?
912  if (pNewClientInfo->IsAddPacket())
913  {
914  (*ppExistingInfo)->GrabMergeFrom(*pNewClientInfo);
915  // info added: remove unused class
916  delete pNewClientInfo;
917  // assign existing info for further usage in this fn
918  return pNewClientInfo = *ppExistingInfo;
919  }
920  // no add packet: overwrite current info
921  delete *ppExistingInfo;
922  return *ppExistingInfo = pNewClientInfo;
923  }
924  // no existing info: add it directly
925  pNewClientInfo->ResetAdd();
926  // may need to grow list (vector) for that
927  if (iClientCount >= iClientCapacity) GrowList(4);
928  ppClients[iClientCount++] = pNewClientInfo;
929  // done; return actual info
930  return pNewClientInfo;
931 }
932 
934 {
935  // search list
936  for (int32_t i=0; i<iClientCount; ++i) if (ppClients[i]->GetClientID() == iClientID) return ppClients+i;
937  // nothing found
938  return nullptr;
939 }
940 
942 {
943  // count players of all clients
944  int32_t iCount=0;
945  for (int32_t i=0; i<iClientCount; ++i)
946  iCount += ppClients[i]->GetPlayerCount();
947  // return it
948  return iCount;
949 }
950 
952 {
953  // count players of all clients
954  int32_t iCount=0;
955  for (int32_t i=0; i<iClientCount; ++i)
956  {
957  C4ClientPlayerInfos *pClient = ppClients[i];
958  for (int32_t j=0; j<pClient->GetPlayerCount(); ++j)
959  if (pClient->GetPlayerInfo(j)->HasJoinIssued())
960  ++iCount;
961  }
962  // return it
963  return iCount;
964 }
965 
967 {
968  // count players of all clients
969  int32_t iCount = 0;
970  for (int32_t i = 0; i<iClientCount; ++i)
971  {
972  C4ClientPlayerInfos *pClient = ppClients[i];
973  for (int32_t j = 0; j<pClient->GetPlayerCount(); ++j)
974  if (pClient->GetPlayerInfo(j)->HasJoinPending())
975  ++iCount;
976  }
977  // return it
978  return iCount;
979 }
980 
981 int32_t C4PlayerInfoList::GetActivePlayerCount(bool fCountInvisible) const
982 {
983  // count players of all clients
984  int32_t iCount=0;
985  for (int32_t i=0; i<iClientCount; ++i)
986  {
987  C4ClientPlayerInfos *pClient = ppClients[i];
988  for (int32_t j=0; j<pClient->GetPlayerCount(); ++j)
989  {
990  C4PlayerInfo *pInfo = pClient->GetPlayerInfo(j);
991  if (!pInfo->IsRemoved())
992  if (fCountInvisible || !pInfo->IsInvisible())
993  ++iCount;
994  }
995  }
996  // return it
997  return iCount;
998 }
999 
1000 int32_t C4PlayerInfoList::GetActiveScriptPlayerCount(bool fCountSavegameResumes, bool fCountInvisible) const
1001 {
1002  // count players of all clients
1003  int32_t iCount=0;
1004  for (int32_t i=0; i<iClientCount; ++i)
1005  {
1006  C4ClientPlayerInfos *pClient = ppClients[i];
1007  for (int32_t j=0; j<pClient->GetPlayerCount(); ++j)
1008  {
1009  C4PlayerInfo *pNfo = pClient->GetPlayerInfo(j);
1010  if (!pNfo->IsRemoved())
1011  if (pNfo->GetType() == C4PT_Script)
1012  if (fCountSavegameResumes || !pNfo->GetAssociatedSavegamePlayerID())
1013  if (fCountInvisible || !pNfo->IsInvisible())
1014  ++iCount;
1015  }
1016  }
1017  // return it
1018  return iCount;
1019 }
1020 
1021 StdStrBuf C4PlayerInfoList::GetActivePlayerNames(bool fCountInvisible, int32_t iAtClientID) const
1022 {
1023  // add up players of all clients
1024  StdStrBuf sPlr;
1025  int32_t iCount=0;
1026  for (int32_t i=0; i<iClientCount; ++i)
1027  {
1028  C4ClientPlayerInfos *pClient = ppClients[i];
1029  if (iAtClientID != -1 && pClient->GetClientID() != iAtClientID) continue;
1030  for (int32_t j=0; j<pClient->GetPlayerCount(); ++j)
1031  {
1032  C4PlayerInfo *pInfo = pClient->GetPlayerInfo(j);
1033  if (!pInfo->IsRemoved()) if (fCountInvisible || !pInfo->IsInvisible())
1034  {
1035  if (iCount++)
1036  {
1037  // not first name: Add separator
1038  sPlr.Append(", ");
1039  }
1040  sPlr.Append(pInfo->GetName());
1041  }
1042  }
1043  }
1044  // return it
1045  return sPlr;
1046 }
1047 
1049 {
1050  // check all packets for a player
1051  for (int32_t i=0; i<iClientCount; ++i)
1052  {
1053  int32_t j=0; C4PlayerInfo *pInfo;
1054  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1055  if (index-- <= 0)
1056  return pInfo;
1057  }
1058  // nothing found
1059  return nullptr;
1060 }
1061 
1063 {
1064  // must be a valid ID
1065  assert(id);
1066  // check all packets for a player
1067  for (int32_t i=0; i<iClientCount; ++i)
1068  {
1069  int32_t j=0; C4PlayerInfo *pInfo;
1070  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1071  if (pInfo->GetID() == id) return pInfo;
1072  }
1073  // nothing found
1074  return nullptr;
1075 }
1076 
1078 {
1079  // get client info that contains a specific player
1080  assert(id);
1081  for (int32_t i=0; i<iClientCount; ++i)
1082  {
1083  int32_t j=0; C4PlayerInfo *pInfo;
1084  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1085  if (pInfo->GetID() == id) return ppClients[i];
1086  }
1087  // nothing found
1088  return nullptr;
1089 }
1090 
1091 C4PlayerInfo *C4PlayerInfoList::GetPlayerInfoByID(int32_t id, int32_t *pidClient) const
1092 {
1093  // must be a valid ID
1094  assert(id); assert(pidClient);
1095  // check all packets for a player
1096  for (int32_t i=0; i<iClientCount; ++i)
1097  {
1098  int32_t j=0; C4PlayerInfo *pInfo;
1099  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1100  if (pInfo->GetID() == id)
1101  {
1102  *pidClient = ppClients[i]->GetClientID();
1103  return pInfo;
1104  }
1105  }
1106  // nothing found
1107  return nullptr;
1108 }
1109 
1111 {
1112  // must be a valid ID
1113  assert(id);
1114  // check all packets for a player
1115  for (int32_t i=0; i<iClientCount; ++i)
1116  {
1117  int32_t j=0; C4PlayerInfo *pInfo;
1118  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1119  if (pInfo->GetAssociatedSavegamePlayerID() == id) return pInfo;
1120  }
1121  // nothing found
1122  return nullptr;
1123 }
1124 
1126 {
1127  // check all packets for players
1128  C4PlayerInfo *pSmallest=nullptr;
1129  for (int32_t i=0; i<iClientCount; ++i)
1130  {
1131  int32_t j=0; C4PlayerInfo *pInfo;
1132  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1133  if (pInfo->GetID() > id)
1134  if (!pSmallest || pSmallest->GetID()>pInfo->GetID())
1135  pSmallest = pInfo;
1136  }
1137  // return best found
1138  return pSmallest;
1139 }
1140 
1142 {
1143  // check all packets for matching players
1144  for (int32_t i=0; i<iClientCount; ++i)
1145  {
1146  int32_t j=0; C4PlayerInfo *pInfo;
1147  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1148  if (!pInfo->IsRemoved())
1149  if (SEqualNoCase(szName, pInfo->GetName()))
1150  return pInfo;
1151  }
1152  // nothing found
1153  return nullptr;
1154 }
1155 
1156 C4PlayerInfo *C4PlayerInfoList::FindSavegameResumePlayerInfo(const C4PlayerInfo *pMatchInfo, MatchingLevel mlMatchStart, MatchingLevel mlMatchEnd) const
1157 {
1158  assert(pMatchInfo);
1159  // try different matching levels using the infamous for-case-paradigm
1160  for (int iMatchLvl = mlMatchStart; iMatchLvl <= mlMatchEnd; ++iMatchLvl)
1161  {
1162  for (int32_t i=0; i<iClientCount; ++i)
1163  {
1164  int32_t j=0; C4PlayerInfo *pInfo;
1165  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1166  if (!Game.PlayerInfos.GetPlayerInfoByID(pInfo->GetID()) && !Game.PlayerInfos.GetPlayerInfoBySavegameID(pInfo->GetID())) // only unassigned player infos
1167  switch (iMatchLvl)
1168  {
1169  case PML_PlrFileName: // file name and player name must match
1170  if (!pMatchInfo->GetFilename() || !pInfo->GetFilename()) break;
1171  if (!SEqualNoCase(GetFilename(pMatchInfo->GetFilename()), GetFilename(pInfo->GetFilename()))) break;
1172  // nobreak: Check player name as well
1173  case PML_PlrName: // match player name
1174  if (SEqualNoCase(pMatchInfo->GetName(), pInfo->GetName()))
1175  return pInfo;
1176  break;
1177  case PML_PrefColor: // match player color
1178  if (pMatchInfo->GetOriginalColor() == pInfo->GetOriginalColor())
1179  return pInfo;
1180  break;
1181  case PML_Any: // match anything
1182  return pInfo;
1183  }
1184  }
1185  }
1186  // no match
1187  return nullptr;
1188 }
1189 
1191 {
1192  // search given list for a player that's not associated locally
1193  C4ClientPlayerInfos *pRestoreClient; int32_t iClient=0;
1194  while ((pRestoreClient = rRestoreInfoList.GetIndexedInfo(iClient++)))
1195  {
1196  C4PlayerInfo *pRestoreInfo; int32_t iInfo=0;
1197  while ((pRestoreInfo = pRestoreClient->GetPlayerInfo(iInfo++)))
1198  if (pRestoreInfo->IsJoined())
1199  // match association either by savegame ID (before C4Game::InitPlayers) or real ID (after C4Game::InitPlayers)
1200  if (!GetPlayerInfoBySavegameID(pRestoreInfo->GetID()) && !GetPlayerInfoByID(pRestoreInfo->GetID()))
1201  return pRestoreInfo;
1202  }
1203  // no unassociated info found
1204  return nullptr;
1205 }
1206 
1207 bool C4PlayerInfoList::HasSameTeamPlayers(int32_t iClient1, int32_t iClient2) const
1208 {
1209  // compare all player teams of clients
1210  const C4ClientPlayerInfos *pCnfo1 = GetInfoByClientID(iClient1);
1211  const C4ClientPlayerInfos *pCnfo2 = GetInfoByClientID(iClient2);
1212  if (!pCnfo1 || !pCnfo2) return false;
1213  int32_t i=0,j; const C4PlayerInfo *pNfo1, *pNfo2;
1214  while ((pNfo1 = pCnfo1->GetPlayerInfo(i++)))
1215  {
1216  if (!pNfo1->IsUsingTeam()) continue;
1217  j=0;
1218  while ((pNfo2 = pCnfo2->GetPlayerInfo(j++)))
1219  {
1220  if (!pNfo2->IsUsingTeam()) continue;
1221  if (pNfo2->GetTeam() == pNfo1->GetTeam())
1222  // match found!
1223  return true;
1224  }
1225  }
1226  // no match
1227  return false;
1228 }
1229 
1230 bool C4PlayerInfoList::Load(C4Group &hGroup, const char *szFromFile, C4LangStringTable *pLang)
1231 {
1232  // clear previous
1233  Clear();
1234  // load file contents
1235  StdStrBuf Buf;
1236  if (!hGroup.LoadEntryString(szFromFile, &Buf))
1237  // no file is OK; means no player infos
1238  return true;
1239  // replace strings
1240  if (pLang) pLang->ReplaceStrings(Buf);
1241  // (try to) compile
1242  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(
1243  mkNamingAdapt(*this, "PlayerInfoList"),
1244  Buf, szFromFile))
1245  return false;
1246  // done, success
1247  return true;
1248 }
1249 
1250 bool C4PlayerInfoList::Save(C4Group &hGroup, const char *szToFile)
1251 {
1252  // remove previous entry from group
1253  hGroup.DeleteEntry(szToFile);
1254  // anything to save?
1255  if (!iClientCount) return true;
1256  // save it
1257  try
1258  {
1259  // decompile
1260  StdStrBuf Buf = DecompileToBuf<StdCompilerINIWrite>(
1261  mkNamingAdapt(*this, "PlayerInfoList"));
1262  // save buffer to group
1263  hGroup.Add(szToFile, Buf, false, true);
1264  }
1265  catch (StdCompiler::Exception *)
1266  { return false; }
1267  // done, success
1268  return true;
1269 }
1270 
1272 {
1273  // not in replay
1274  if (Game.C4S.Head.Replay) return true;
1275  // no double init
1276  assert(!GetInfoCount());
1277  // no network
1278  assert(!::Network.isEnabled());
1279  // create player info for local player joins
1281  // register local info immediately
1282  pLocalInfo = AddInfo(pLocalInfo);
1283  // Script players in restore infos need to be associated with matching script players in main info list
1285  // and assign teams
1286  if (Game.Teams.IsMultiTeams() && pLocalInfo)
1287  AssignTeams(pLocalInfo, true);
1288  // done, success
1289  return true;
1290 }
1291 
1293 {
1294  // local call only - in network, C4Network2Players joins players!
1295  assert(!::Network.isEnabled());
1296  // get local players
1297  C4ClientPlayerInfos **ppkLocal = GetInfoPtrByClientID(::Control.ClientID()), *pkLocal;
1298  if (!ppkLocal) return false;
1299  pkLocal = *ppkLocal;
1300  // check all players
1301  int32_t i=0; C4PlayerInfo *pInfo;
1302  while ((pInfo = pkLocal->GetPlayerInfo(i++)))
1303  // not yet joined?
1304  if (!pInfo->HasJoinIssued())
1305  {
1306  // join will be marked when queue is executed (C4Player::Join)
1307  // but better mark join now already to prevent permanent sending overkill
1308  pInfo->SetJoinIssued();
1309  // join by filename if possible. Script players may not have a filename assigned though
1310  const char *szFilename = pInfo->GetFilename();
1311  if (!szFilename && (pInfo->GetType() != C4PT_Script))
1312  {
1313  // failure for user players
1314  const char *szPlrName = pInfo->GetName(); if (!szPlrName) szPlrName="???";
1315  LogF(LoadResStr("IDS_ERR_JOINQUEUEPLRS"), szPlrName);
1316  continue;
1317  }
1319  new C4ControlJoinPlayer(szFilename, ::Control.ClientID(), pInfo->GetID()));
1320  }
1321  // done, success
1322  return true;
1323 }
1324 
1326 {
1327  // create matching script player joins for all script playeers in restore info
1328  // Just copy their infos to the first client
1329  int32_t i;
1330  C4ClientPlayerInfos *pHostInfo = GetIndexedInfo(0);
1331  for (i=0; i<rSavegamePlayers.GetInfoCount(); ++i)
1332  {
1333  C4ClientPlayerInfos *pkInfo = rSavegamePlayers.GetIndexedInfo(i);
1334  int32_t j=0; C4PlayerInfo *pInfo;
1335  while ((pInfo = pkInfo->GetPlayerInfo(j++)))
1336  if (pInfo->GetType() == C4PT_Script)
1337  {
1338  // safety
1339  C4PlayerInfo *pRejoinInfo;
1340  if ((pRejoinInfo = GetPlayerInfoBySavegameID(pInfo->GetID())))
1341  {
1342  LogF("Warning: User player %s takes over script player %s!", pRejoinInfo->GetName(), pInfo->GetName());
1343  continue;
1344  }
1345  if (!pHostInfo)
1346  {
1347  LogF("Error restoring savegame script players: No host player infos to add to!");
1348  continue;
1349  }
1350  // generate takeover info
1351  pRejoinInfo = new C4PlayerInfo(*pInfo);
1352  pRejoinInfo->SetAssociatedSavegamePlayer(pInfo->GetID());
1353  pHostInfo->AddInfo(pRejoinInfo);
1354  }
1355  }
1356  // teams must recognize the change
1358 }
1359 
1361 {
1362  // any un-associated players?
1363  if (rSavegamePlayers.GetPlayerCount())
1364  {
1365  // for runtime network joins, this should never happen!
1366  assert(!Game.C4S.Head.NetworkRuntimeJoin);
1367 
1368  // do savegame player association of real players
1369  // for non-lobby games do automatic association first
1370  int32_t iNumGrabbed = 0, i;
1371  if (!::Network.isEnabled() && Game.C4S.Head.SaveGame)
1372  {
1373  // do several passes: First passes using regular player matching; following passes matching anything but with a warning message
1374  for (int eMatchingLevel = 0; eMatchingLevel <= PML_Any; ++eMatchingLevel)
1375  for (int32_t i=0; i<iClientCount; ++i)
1376  {
1377  C4ClientPlayerInfos *pkInfo = GetIndexedInfo(i);
1378  int32_t j=0, id; C4PlayerInfo *pInfo, *pSavegameInfo;
1379  while ((pInfo = pkInfo->GetPlayerInfo(j++)))
1380  if (!(id = pInfo->GetAssociatedSavegamePlayerID()))
1381  if ((pSavegameInfo = rSavegamePlayers.FindSavegameResumePlayerInfo(pInfo, (MatchingLevel)eMatchingLevel, (MatchingLevel)eMatchingLevel)))
1382  {
1383  pInfo->SetAssociatedSavegamePlayer(pSavegameInfo->GetID());
1384  if (eMatchingLevel > PML_PlrName)
1385  {
1386  // this is a "wild" match: Warn the player (but not in replays)
1387  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_MSG_PLAYERASSIGNMENT"), pInfo->GetName(), pSavegameInfo->GetName());
1388  Log(sMsg.getData());
1389  if (::pGUI && FullScreen.Active && !Game.C4S.Head.Replay)
1391  }
1392  }
1393  }
1394  }
1395  // association complete: evaluate it
1396  for (i=0; i<iClientCount; ++i)
1397  {
1398  C4ClientPlayerInfos *pkInfo = GetIndexedInfo(i);
1399  int32_t j=0, id; C4PlayerInfo *pInfo, *pSavegameInfo;
1400  while ((pInfo = pkInfo->GetPlayerInfo(j++)))
1401  if ((id = pInfo->GetAssociatedSavegamePlayerID()))
1402  {
1403  if ((pSavegameInfo = rSavegamePlayers.GetPlayerInfoByID(id)))
1404  {
1405  // pInfo continues for pSavegameInfo
1406  pInfo->SetSavegameResume(pSavegameInfo);
1407  ++iNumGrabbed;
1408  }
1409  else
1410  {
1411  // shouldn't happen
1412  assert(!"Invalid savegame association");
1413  }
1414  }
1415  else
1416  {
1417  // no association for this info: Joins as new player
1418  // in savegames, this is unusual. For regular script player restore, it's not
1419  if (Game.C4S.Head.SaveGame) LogF(LoadResStr("IDS_PRC_RESUMENOPLRASSOCIATION"), (const char *)pInfo->GetName());
1420  }
1421  }
1422  // otherwise any remaining players
1423  int32_t iCountRemaining = rSavegamePlayers.GetPlayerCount() - iNumGrabbed;
1424  if (iCountRemaining)
1425  {
1426  // in replay mode, if there are no regular player joins, it must have been a runtime record
1427  // i.e., a record that was started during the game
1428  // in this case, the savegame player infos equal the real player infos to be used
1429  if (::Control.isReplay() && !GetInfoCount())
1430  {
1431  *this = rSavegamePlayers;
1432  }
1433  else
1434  {
1435  // in regular mode, these players must be removed
1436  LogF(LoadResStr("IDS_PRC_RESUMEREMOVEPLRS"), iCountRemaining);
1437  // remove them directly from the game
1438  RemoveUnassociatedPlayers(rSavegamePlayers);
1439  }
1440  }
1441  }
1442  // now that players are restored, restore teams
1444  // done, success
1445  return true;
1446 }
1447 
1449 {
1450  // Note that this method will be called on the main list for savegame resumes (even in network) or regular games with RecreateInfos,
1451  // and on RestorePlayerInfos for runtime network joins
1452  // check all player files that need to be recreated
1453  for (int32_t i=0; i<iClientCount; ++i)
1454  {
1455  C4ClientPlayerInfos *pkInfo = ppClients[i];
1456  int32_t j=0; C4PlayerInfo *pInfo;
1457  while ((pInfo = pkInfo->GetPlayerInfo(j++)))
1458  if (pInfo->IsJoined())
1459  {
1460  // all players in replays and runtime joins; script players even in savegames need to be restored from the scenario goup
1462  {
1463  // in this case, a filename must have been assigned while saving
1464  // and mark a file inside the scenario file
1465  // get filename of joined player - this should always be valid!
1466  const char *szCurrPlrFile;
1467  StdStrBuf sFilenameInRecord;
1468  if (Game.C4S.Head.Replay)
1469  {
1470  // replay of resumed savegame: RecreatePlayers saves used player files into the record group in this manner
1471  sFilenameInRecord.Format("Recreate-%d.ocp", pInfo->GetID());
1472  szCurrPlrFile = sFilenameInRecord.getData();
1473  }
1474  else
1475  szCurrPlrFile = pInfo->GetFilename();
1476  const char *szPlrName = pInfo->GetName(); if (!szPlrName) szPlrName = "???";
1477  if (!szCurrPlrFile || !*szCurrPlrFile)
1478  {
1479  // that's okay for script players, because those may join w/o recreation files
1480  if (pInfo->GetType() != C4PT_Script)
1481  {
1482  LogF(LoadResStr("IDS_ERR_LOAD_RECR_NOFILE"), szPlrName);
1483  }
1484  continue;
1485  }
1486  // join from temp file
1487  StdCopyStrBuf szJoinPath;
1488  szJoinPath = Config.AtTempPath(GetFilename(szCurrPlrFile));
1489  // extract player there
1490  if (!Game.ScenarioFile.FindEntry(GetFilename(szCurrPlrFile)) || !Game.ScenarioFile.Extract(GetFilename(szCurrPlrFile), szJoinPath.getData()))
1491  {
1492  // that's okay for script players, because those may join w/o recreation files
1493  if (pInfo->GetType() != C4PT_Script)
1494  {
1495  LogF(LoadResStr("IDS_ERR_LOAD_RECR_NOEXTRACT"), szPlrName, GetFilename(szCurrPlrFile));
1496  }
1497  continue;
1498  }
1499  // set join source
1500  pInfo->SetFilename(szJoinPath.getData());
1501  pInfo->DiscardResource();
1502  // setting a temp file here will cause the player file to be deleted directly after recreation
1503  // if recreation fails (e.g. the game gets aborted due to invalid files), the info dtor will delete the file
1504  pInfo->SetTempFile();
1505  }
1506  else
1507  {
1508  // regular player in savegame being resumed in network or normal mode:
1509  // the filenames and/or resources should have been assigned
1510  // a) either in lobby mode during player re-acquisition
1511  // b) or when players from rSavegamePlayers were taken over
1512  }
1513  }
1514  else if (!pInfo->HasJoinIssued())
1515  {
1516  // new players to be joined into the game:
1517  // regular control queue join can be done; no special handling needed
1518  }
1519  }
1520  // done, success
1521  return true;
1522 }
1523 
1525 {
1526  // check all player infos
1527  for (int32_t i=0; i<iClientCount; ++i)
1528  {
1529  C4ClientPlayerInfos *pkInfo = ppClients[i];
1530  // skip clients without joined players
1531  if (!pkInfo->GetJoinedPlayerCount()) continue;
1532  // determine client ID and name
1533  // client IDs must be set correctly even in replays,
1534  // so client-removal packets are executed correctly
1535  int32_t idAtClient = pkInfo->GetClientID();
1536  const char *szAtClientName;
1537  if (Game.C4S.Head.Replay)
1538  // the client name can currently not really be retrieved in replays
1539  // but it's not used anyway
1540  szAtClientName = "Replay";
1541  else
1542  // local non-network non-replay games set local name
1543  if (!::Network.isEnabled())
1544  {
1545  assert(idAtClient == ::Control.ClientID());
1546  szAtClientName = "Local";
1547  }
1548  else
1549  {
1550  // network non-replay games: find client and set name by it
1551  const C4Client *pClient = Game.Clients.getClientByID(idAtClient);
1552  if (pClient)
1553  szAtClientName = pClient->getName();
1554  else
1555  {
1556  // this shouldn't happen - remove the player info
1557  LogF(LoadResStr("IDS_PRC_RESUMENOCLIENT"), idAtClient, pkInfo->GetPlayerCount());
1558  continue;
1559  }
1560  }
1561  // rejoin all joined players of that client
1562  int32_t j=0; C4PlayerInfo *pInfo;
1563  while ((pInfo = pkInfo->GetPlayerInfo(j++)))
1564  if (pInfo->IsJoined())
1565  {
1566  // get filename to join from
1567  const char *szFilename = pInfo->GetLocalJoinFilename();
1568  // ensure resource is loaded, if joining from resource
1569  // this may display a waiting dialog and block the thread for a while
1570  C4Network2Res *pJoinRes = pInfo->GetRes();
1571  if (szFilename && pJoinRes && pJoinRes->isLoading())
1572  {
1573  const char *szName = pInfo->GetName();
1574  if (!::Network.RetrieveRes(pJoinRes->getCore(), C4NetResRetrieveTimeout,
1575  FormatString(LoadResStr("IDS_NET_RES_PLRFILE"), szName).getData()))
1576  szFilename=nullptr;
1577  }
1578  // file present?
1579  if (!szFilename || !*szFilename)
1580  {
1581  if (pInfo->GetType() == C4PT_User)
1582  {
1583  // for user players, this could happen only if the user cancelled the resource
1584  const char *szPlrName = pInfo->GetName(); if (!szPlrName) szPlrName = "???";
1585  LogF(LoadResStr("IDS_ERR_LOAD_RECR_NOFILEFROMNET"), szPlrName);
1586  continue;
1587  }
1588  else
1589  {
1590  // for script players: Recreation without filename OK
1591  szFilename = nullptr;
1592  }
1593  }
1594  // record file handling: Save to the record file in the manner it's expected by C4PlayerInfoList::RecreatePlayers
1595  if (::Control.isRecord() && szFilename)
1596  {
1597  StdStrBuf sFilenameInRecord;
1598  sFilenameInRecord.Format("Recreate-%d.ocp", pInfo->GetID());
1599  ::Control.RecAddFile(szFilename, sFilenameInRecord.getData());
1600  }
1601  // recreate join directly
1602  ::Players.Join(szFilename, false, idAtClient, szAtClientName, pInfo, numbers);
1603  // delete temporary files immediately
1604  if (pInfo->IsTempFile()) pInfo->DeleteTempFile();
1605  }
1606  }
1607  // done!
1608  return true;
1609 }
1610 
1612 {
1613  // check all joined infos
1614  C4ClientPlayerInfos *pClient; int iClient=0;
1615  while ((pClient = rSavegamePlayers.GetIndexedInfo(iClient++)))
1616  {
1617  C4PlayerInfo *pInfo; int iInfo = 0;
1618  while ((pInfo = pClient->GetPlayerInfo(iInfo++)))
1619  {
1620  // remove players that were in the game but are not associated
1621  if (pInfo->IsJoined() && !GetPlayerInfoBySavegameID(pInfo->GetID()))
1622  {
1623  if (::Players.RemoveUnjoined(pInfo->GetInGameNumber()))
1624  {
1625  LogF(LoadResStr("IDS_PRC_REMOVEPLR"), pInfo->GetName());
1626  }
1627  }
1628  pInfo->SetRemoved();
1629  }
1630  }
1631 }
1632 
1633 bool C4PlayerInfoList::SetAsRestoreInfos(C4PlayerInfoList &rFromPlayers, bool fSaveUserPlrs, bool fSaveScriptPlrs, bool fSetUserPlrRefToLocalGroup, bool fSetScriptPlrRefToLocalGroup)
1634 {
1635  // copy everything
1636  *this = rFromPlayers;
1637  // then remove everything that's no longer joined and update the rest
1638  C4ClientPlayerInfos *pClient; int iClient=0;
1639  while ((pClient = GetIndexedInfo(iClient++)))
1640  {
1641  // update all players for this client
1642  C4PlayerInfo *pInfo; int iInfo = 0;
1643  while ((pInfo = pClient->GetPlayerInfo(iInfo++)))
1644  {
1645  bool fKeepInfo = false;
1646  // remove players that are not in the game
1647  if (pInfo->IsJoined())
1648  {
1649  // pre-reset filename
1650  pInfo->SetFilename(nullptr);
1651  if (pInfo->GetType() == C4PT_User)
1652  {
1653  fKeepInfo = fSaveUserPlrs;
1654  if (fSetUserPlrRefToLocalGroup)
1655  {
1656  // in the game: Set filename for inside savegame file
1657  StdStrBuf sNewName;
1658  if (::Network.isEnabled())
1659  {
1660  C4Client *pGameClient = Game.Clients.getClientByID(pClient->GetClientID());
1661  const char *szName = pGameClient ? pGameClient->getName() : "Unknown";
1662  sNewName.Format("%s-%s", szName, (const char *) GetFilename(pInfo->GetLocalJoinFilename()));
1663  }
1664  else
1665  sNewName.Copy(GetFilename(pInfo->GetFilename()));
1666 
1667  // O(n) is fast.
1668  // If not, blame whoever wrote Replace! ;)
1669  sNewName.Replace("%", "%25", 0);
1670  for (int ch = 128; ch < 256; ++ch)
1671  {
1672  const char* hexChars = "0123456789abcdef";
1673  char old[] = { char(ch), 0 };
1674  char safe[] = { '%', 'x', 'x', 0 };
1675  safe[1] = hexChars[ch / 16];
1676  safe[2] = hexChars[ch % 16];
1677  sNewName.Replace(old, safe, 0);
1678  }
1679 
1680  pInfo->SetFilename(sNewName.getData());
1681  }
1682  }
1683  else if (pInfo->GetType() == C4PT_Script)
1684  {
1685  // Save only if either all players should be saved (fSaveScriptPlrs && fSaveUserPlrs)
1686  // or if script players are saved and general scenario saving for this script player is desired
1687  fKeepInfo = fSaveScriptPlrs && (fSaveUserPlrs || pInfo->IsScenarioSaveDesired());
1688  if (fSetScriptPlrRefToLocalGroup)
1689  {
1690  // just compose a unique filename for script player
1691  pInfo->SetFilename(FormatString("ScriptPlr-%d.ocp", (int)pInfo->GetID()).getData());
1692  }
1693  }
1694  }
1695  if (!fKeepInfo)
1696  {
1697  pClient->RemoveIndexedInfo(--iInfo);
1698  }
1699  else
1700  {
1701  pInfo->DiscardResource();
1702  }
1703  }
1704  // remove empty clients
1705  if (!pClient->GetPlayerCount())
1706  {
1708  delete pClient;
1709  --iClient;
1710  }
1711  }
1712  // done
1713  return true;
1714 }
1715 
1717 {
1718  C4ClientPlayerInfos *pClient; int iClient=0;
1719  while ((pClient = GetIndexedInfo(iClient++)))
1720  {
1721  C4PlayerInfo *pInfo; int iInfo = 0;
1722  while ((pInfo = pClient->GetPlayerInfo(iInfo++)))
1723  if (pInfo->IsLeagueProjectedGainValid())
1724  {
1725  pInfo->ResetLeagueProjectedGain();
1726  if (fSetUpdated)
1727  pClient->SetUpdated();
1728  }
1729  }
1730 }
1731 
1733 {
1734  bool deserializing = pComp->isDeserializer();
1735  if (deserializing) Clear();
1736  // skip compiling if there is nothing to compile (cosmentics)
1737  if (!deserializing && pComp->hasNaming() && iLastPlayerID == 0 && iClientCount == 0)
1738  return;
1739  // header
1740  pComp->Value(mkNamingAdapt(iLastPlayerID, "LastPlayerID", 0));
1741  // client count
1742  int32_t iTemp = iClientCount;
1743  pComp->Value(mkNamingCountAdapt<int32_t>(iTemp, "Client"));
1744  if (iTemp < 0 || iTemp > C4MaxClient)
1745  { pComp->excCorrupt("client count out of range"); return; }
1746  // grow list
1747  if (deserializing)
1748  {
1749  if (iTemp > iClientCapacity) GrowList(iTemp - iClientCapacity);
1750  iClientCount = iTemp;
1751  ZeroMem(ppClients, sizeof(*ppClients) * iClientCount);
1752  }
1753  // client packets
1754  pComp->Value(
1755  mkNamingAdapt(
1756  mkArrayAdaptMap(ppClients, iClientCount, mkPtrAdaptNoNull<C4ClientPlayerInfos>),
1757  "Client"));
1758  // force compiler to specialize
1759  mkPtrAdaptNoNull<C4ClientPlayerInfos>(*ppClients);
1760 }
1761 
1763 {
1764  // count all joined and to-be-joined
1765  int32_t iCnt=0;
1766  for (int32_t i=0; i<iClientCount; ++i)
1767  {
1768  int32_t j=0; C4PlayerInfo *pInfo;
1769  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1770  if (!pInfo->IsRemoved()) ++iCnt;
1771  }
1772  return iCnt;
1773 }
1774 
1776 {
1777  // load for all players
1778  int32_t i = iClientCount; C4ClientPlayerInfos **ppClient=ppClients;
1779  while (i--) (*ppClient++)->LoadResources();
1780 }
1781 
1783 {
1784  // make sure ID counter is same as largest info
1785  for (int32_t i=0; i<iClientCount; ++i)
1786  {
1787  int32_t j=0; C4PlayerInfo *pInfo;
1788  while ((pInfo = ppClients[i]->GetPlayerInfo(j++)))
1789  {
1790  iLastPlayerID = std::max<int32_t>(pInfo->GetID(), iLastPlayerID);
1791  }
1792  }
1793 }
1794 
1795 
1796 /* -- Player info packets -- */
1797 
1799 {
1800  pComp->Value(Info);
1801 }
1802 
1804 {
1805  pComp->Value(mkNamingAdapt(fIsRecreationInfo, "Recreation", false));
1806  pComp->Value(mkNamingAdapt(Info, "Info"));
1807 }
const int32_t C4ClientIDUnknown
Definition: C4Client.h:24
#define C4CFN_BigIcon
Definition: C4Components.h:111
C4Config Config
Definition: C4Config.cpp:930
C4PlayerType
Definition: C4Constants.h:152
@ C4PT_User
Definition: C4Constants.h:154
@ C4PT_Script
Definition: C4Constants.h:155
const int C4FCT_Full
Definition: C4FacetEx.h:26
C4GameControl Control
@ CDT_Direct
Definition: C4GameControl.h:36
@ CDT_Queue
Definition: C4GameControl.h:34
C4Game Game
Definition: C4Globals.cpp:52
C4Network2 Network
Definition: C4Globals.cpp:53
C4FullScreen FullScreen
Definition: C4Globals.cpp:46
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
const int C4NetResRetrieveTimeout
Definition: C4Network2.h:37
@ NRT_Player
Definition: C4Network2Res.h:45
const int32_t C4NetResMaxBigicon
Definition: C4Network2Res.h:36
@ CID_JoinPlr
Definition: C4PacketBase.h:159
@ CID_PlrInfo
Definition: C4PacketBase.h:158
const int32_t C4MaxPlayer
Definition: C4Player.h:37
const int32_t C4MaxClient
Definition: C4Player.h:38
C4PlayerList Players
C4Reloc Reloc
Definition: C4Reloc.cpp:21
#define _MAX_PATH
#define _MAX_PATH_LEN
uint32_t DWORD
const char * SSearch(const char *szString, const char *szIndex)
Definition: Standard.cpp:369
bool SGetModule(const char *szList, int iIndex, char *sTarget, int iSize)
Definition: Standard.cpp:539
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:213
int SModuleCount(const char *szList)
Definition: Standard.cpp:617
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:60
StdBitfieldAdapt< T > mkBitfieldAdapt(T &rVal, const StdBitfieldEntry< T > *pNames)
Definition: StdAdaptors.h:985
StdArrayAdapt< T, M > mkArrayAdaptMap(T *pArray, int iSize, M &&map)
Definition: StdAdaptors.h:340
StdIntPackAdapt< T > mkIntPackAdapt(T &rVal)
Definition: StdAdaptors.h:791
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:490
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
Definition: StdAdaptors.h:883
Definition: StdAdaptors.h:795
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
bool EraseItem(const char *szItemName)
Definition: StdFile.cpp:833
char * GetFilename(char *szPath)
Definition: StdFile.cpp:42
int iCnt
Definition: TstC4NetIO.cpp:32
const char * getName() const
Definition: C4Client.h:107
C4Client * getClientByID(int32_t iID) const
Definition: C4Client.cpp:200
void RemoveInfo(int32_t idPlr)
void CompileFunc(StdCompiler *pComp)
void AddInfo(C4PlayerInfo *pAddInfo)
C4ClientPlayerInfos & operator=(const C4ClientPlayerInfos &rCopy)
int32_t GetClientID() const
Definition: C4PlayerInfo.h:257
C4PlayerInfo * GetPlayerInfoByID(int32_t id) const
C4ClientPlayerInfos(const char *szJoinFilenames=nullptr, bool fAdd=false, C4PlayerInfo *pAddInfo=nullptr)
void RemoveIndexedInfo(int32_t iAtIndex)
void GrabMergeFrom(C4ClientPlayerInfos &rFrom)
C4PlayerInfo * GetPlayerInfo(int32_t iIndex) const
int32_t GetPlayerCount() const
Definition: C4PlayerInfo.h:251
bool IsAddPacket() const
Definition: C4PlayerInfo.h:260
int32_t GetJoinedPlayerCount() const
bool HasUnjoinedPlayers() const
int32_t GetFlaggedPlayerCount(DWORD dwFlag) const
C4PlayerInfo * GetPlayerInfoByRes(int32_t idResID) const
const char * GetRegistrationData(const char *field)
Definition: C4Config.h:285
C4ConfigStartup Startup
Definition: C4Config.h:264
const char * AtTempPath(const char *filename)
Definition: C4Config.cpp:600
int32_t HideMsgPlrTakeOver
Definition: C4Config.h:189
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:84
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
bool isReplay() const
Definition: C4GameControl.h:98
bool RecAddFile(const char *szLocalFilename, const char *szAddAs)
bool isRecord() const
bool isCtrlHost() const
Definition: C4GameControl.h:99
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
int32_t ClientID() const
bool isNetwork() const
Definition: C4GameControl.h:97
C4TeamList & Teams
Definition: C4Game.h:70
C4Scenario C4S
Definition: C4Game.h:74
char PlayerFilenames[20 *_MAX_PATH_LEN]
Definition: C4Game.h:104
bool IsRunning
Definition: C4Game.h:140
C4ClientList & Clients
Definition: C4Game.h:69
C4PlayerInfoList & PlayerInfos
Definition: C4Game.h:71
C4PlayerInfoList & RestorePlayerInfos
Definition: C4Game.h:72
C4Control & Input
Definition: C4Game.h:82
int32_t FrameCounter
Definition: C4Game.h:129
C4GameParameters & Parameters
Definition: C4Game.h:67
C4Group ScenarioFile
Definition: C4Game.h:86
bool DeleteEntry(const char *filename, bool do_recycle=false)
Definition: C4Group.cpp:1695
bool Extract(const char *files, const char *destination=nullptr, const char *exclude=nullptr)
Definition: C4Group.cpp:1808
bool AccessEntry(const char *wildcard, size_t *size=nullptr, char *filename=nullptr, bool needs_to_be_a_group=false)
Definition: C4Group.cpp:2104
bool Add(const char *filename, const char *entry_name)
Definition: C4Group.cpp:1621
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
Definition: C4Group.cpp:2430
bool FindEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr)
Definition: C4Group.cpp:2211
bool Open(const char *group_name, bool do_create=false)
Definition: C4Group.cpp:660
Definition: C4Id.h:26
static const C4ID None
Definition: C4Id.h:39
void ReplaceStrings(StdStrBuf &rBuf)
C4Network2Players Players
Definition: C4Network2.h:119
C4Network2ResList ResList
Definition: C4Network2.h:113
bool isEnabled() const
Definition: C4Network2.h:203
bool isHost() const
Definition: C4Network2.h:209
C4Network2Res::Ref RetrieveRes(const C4Network2ResCore &Core, int32_t iTimeout, const char *szResName, bool fWaitForCore=false)
void RequestPlayerInfoUpdate(const class C4ClientPlayerInfos &rRequest)
int32_t getID() const
Definition: C4Network2Res.h:86
const char * getFile() const
int32_t getResID() const
const C4Network2ResCore & getCore() const
bool isComplete() const
bool isLoading() const
C4Network2Res::Ref getRefRes(int32_t iResID)
C4Network2Res::Ref AddByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID=-1, const char *szResName=nullptr, bool fAllowUnloadable=false)
C4Network2Res::Ref AddByCore(const C4Network2ResCore &Core, bool fLoad=true)
C4ClientPlayerInfos Info
Definition: C4PlayerInfo.h:293
void CompileFunc(StdCompiler *pComp) override
C4ClientPlayerInfos Info
Definition: C4PlayerInfo.h:277
void CompileFunc(StdCompiler *pComp) override
bool Load(C4Group &hGroup)
Definition: C4InfoCore.cpp:88
uint32_t PrefColor2Dw
Definition: C4InfoCore.h:99
char PrefName[C4MaxName+1]
Definition: C4InfoCore.h:85
uint32_t PrefColorDw
Definition: C4InfoCore.h:99
int32_t GetTeam() const
Definition: C4PlayerInfo.h:195
bool LoadFromLocalFile(const char *szFilename)
bool IsInvisible() const
Definition: C4PlayerInfo.h:175
void CompileFunc(StdCompiler *pComp)
uint32_t GetOriginalColor() const
Definition: C4PlayerInfo.h:155
StdStrBuf GetLobbyName() const
uint32_t GetColor() const
Definition: C4PlayerInfo.h:153
DWORD GetFlags()
Definition: C4PlayerInfo.h:198
void SetJoinIssued()
Definition: C4PlayerInfo.h:112
bool SetSavegameResume(C4PlayerInfo *pSavegameInfo)
bool LoadBigIcon(C4FacetSurface &fctTarget)
bool IsUsingTeam() const
Definition: C4PlayerInfo.h:173
void SetJoined(int32_t iNumber)
void SetID(int32_t iToID)
Definition: C4PlayerInfo.h:114
bool IsRemoved() const
Definition: C4PlayerInfo.h:164
bool IsTempFile() const
Definition: C4PlayerInfo.h:196
void ResetLeagueProjectedGain()
Definition: C4PlayerInfo.h:145
bool HasJoinIssued() const
Definition: C4PlayerInfo.h:167
void SetAssociatedSavegamePlayer(int32_t aidSavegamePlayer)
Definition: C4PlayerInfo.h:124
bool HasJoined() const
Definition: C4PlayerInfo.h:165
bool IsScenarioSaveDesired() const
Definition: C4PlayerInfo.h:177
int32_t GetAssociatedSavegamePlayerID() const
Definition: C4PlayerInfo.h:126
int32_t GetID() const
Definition: C4PlayerInfo.h:194
void SetColor(DWORD dwUseClr)
Definition: C4PlayerInfo.h:115
@ PIF_SavegameTakeoverFlags
Definition: C4PlayerInfo.h:68
@ PIF_JoinedForSavegameOnly
Definition: C4PlayerInfo.h:54
@ PIF_NoEliminationCheck
Definition: C4PlayerInfo.h:60
void SetTempFile()
Definition: C4PlayerInfo.h:119
bool HasWon() const
Definition: C4PlayerInfo.h:181
uint32_t GetLobbyColor() const
void SetFilename(const char *szToFilename)
bool HasTeamWon() const
const char * GetLocalJoinFilename() const
const char * GetName() const
Definition: C4PlayerInfo.h:157
int32_t GetInGameNumber() const
Definition: C4PlayerInfo.h:189
void SetToScenarioFilename(const char *szScenFilename)
bool HasJoinPending() const
Definition: C4PlayerInfo.h:168
C4PlayerType GetType() const
Definition: C4PlayerInfo.h:152
C4Network2Res * GetRes() const
Definition: C4PlayerInfo.h:163
void DeleteTempFile()
bool SetAsScriptPlayer(const char *szName, uint32_t dwColor, uint32_t dwFlags, C4ID idExtra)
bool IsJoined() const
Definition: C4PlayerInfo.h:166
void LoadResource()
bool IsLeagueProjectedGainValid() const
Definition: C4PlayerInfo.h:190
void DiscardResource()
const char * GetFilename() const
Definition: C4PlayerInfo.h:161
StdStrBuf GetActivePlayerNames(bool fCountInvisible, int32_t iAtClientID=-1) const
C4ClientPlayerInfos * GetInfoByClientID(int32_t iClientID) const
Definition: C4PlayerInfo.h:361
int32_t GetJoinIssuedPlayerCount() const
C4ClientPlayerInfos * GetIndexedInfo(int32_t iIndex) const
Definition: C4PlayerInfo.h:358
bool SetAsRestoreInfos(C4PlayerInfoList &rFromPlayers, bool fSaveUserPlrs, bool fSaveScriptPlrs, bool fSetUserPlrRefToLocalGroup, bool fSetScriptPlrRefToLocalGroup)
C4PlayerInfo * GetPlayerInfoByIndex(int32_t index) const
bool RecreatePlayers(C4ValueNumbers *)
bool Load(C4Group &hGroup, const char *szFromFile, class C4LangStringTable *pLang=nullptr)
void AssignTeams(C4ClientPlayerInfos *pNewClientInfo, bool fByHost)
int32_t GetJoinPendingPlayerCount() const
int32_t GetActiveScriptPlayerCount(bool fCountSavegameResumes, bool fCountInvisible) const
void RecheckAutoGeneratedTeams()
int32_t GetFreePlayerSlotCount()
C4PlayerInfo * GetPlayerInfoBySavegameID(int32_t id) const
int32_t GetStartupCount()
bool Save(C4Group &hGroup, const char *szToFile)
C4PlayerInfo * GetNextPlayerInfoByID(int32_t id) const
C4PlayerInfo * FindUnassociatedRestoreInfo(const C4PlayerInfoList &rRestoreInfoList)
void CreateRestoreInfosForJoinedScriptPlayers(C4PlayerInfoList &rSavegamePlayers)
void RemoveUnassociatedPlayers(C4PlayerInfoList &rSavegamePlayers)
C4PlayerInfo * GetActivePlayerInfoByName(const char *szName)
bool DoPlayerInfoUpdate(C4ClientPlayerInfos *pUpdate)
bool HasSameTeamPlayers(int32_t iClient1, int32_t iClient2) const
bool LocalJoinUnjoinedPlayersInQueue()
int32_t GetActivePlayerCount(bool fCountInvisible) const
void RemoveInfo(C4ClientPlayerInfos **ppRemoveInfo)
Definition: C4PlayerInfo.h:386
void ResolvePlayerAttributeConflicts(C4ClientPlayerInfos *pSecPacket)
C4PlayerInfoList & operator=(const C4PlayerInfoList &rCpy)
C4ClientPlayerInfos * GetClientInfoByPlayerID(int32_t id) const
void UpdatePlayerAttributes()
bool RestoreSavegameInfos(C4PlayerInfoList &rSavegamePlayers)
C4PlayerInfo * GetPlayerInfoByID(int32_t id) const
int32_t GetInfoCount() const
Definition: C4PlayerInfo.h:357
C4ClientPlayerInfos * AddInfo(C4ClientPlayerInfos *pNewClientInfo)
void CompileFunc(StdCompiler *pComp)
bool DoLocalNonNetworkPlayerJoin(const char *szPlayerFile)
C4ClientPlayerInfos ** GetInfoPtrByClientID(int32_t iClientID) const
void ResetLeagueProjectedGain(bool fSetUpdated)
C4PlayerInfo * FindSavegameResumePlayerInfo(const C4PlayerInfo *pMatchInfo, MatchingLevel mlMatchStart, MatchingLevel mlMatchEnd) const
int32_t GetPlayerCount() const
bool DoLocalNonNetworkPlayerInfoUpdate(C4ClientPlayerInfos *pUpdate)
bool AssignPlayerIDs(C4ClientPlayerInfos *pNewClientInfo)
C4Player * Join(const char *szFilename, bool fScenarioInit, int iAtClient, const char *szAtClientName, class C4PlayerInfo *pInfo, C4ValueNumbers *)
bool RemoveUnjoined(int32_t iPlayer)
bool Open(C4Group &group, const char *filename) const
Definition: C4Reloc.cpp:156
bool Replay
Definition: C4Scenario.h:72
bool NetworkRuntimeJoin
Definition: C4Scenario.h:79
bool SaveGame
Definition: C4Scenario.h:71
C4SHead Head
Definition: C4Scenario.h:232
Definition: C4Teams.h:31
bool HasWon() const
Definition: C4Teams.cpp:234
uint32_t GetColor() const
Definition: C4Teams.h:70
@ TEAMDIST_RandomInv
Definition: C4Teams.h:112
bool IsMultiTeams() const
Definition: C4Teams.h:162
C4Team * GetTeamByID(int32_t iID) const
Definition: C4Teams.cpp:383
void RecheckPlayers()
Definition: C4Teams.cpp:663
bool IsTeamColors() const
Definition: C4Teams.h:169
TeamDist GetTeamDist() const
Definition: C4Teams.h:211
bool RecheckPlayerInfoTeams(C4PlayerInfo &rNewJoin, bool fByHost)
Definition: C4Teams.cpp:463
C4Team * GetGenerateTeamByID(int32_t iID)
Definition: C4Teams.cpp:390
bool Active
Definition: C4Window.h:274
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:249
void Value(const T &rStruct)
Definition: StdCompiler.h:161
bool isSerializer()
Definition: StdCompiler.h:54
virtual bool isDeserializer()
Definition: StdCompiler.h:53
virtual bool hasNaming()
Definition: StdCompiler.h:58
int Replace(const char *szOld, const char *szNew, size_t iStartSearch=0)
Definition: StdBuf.cpp:284
void Ref(const char *pnData)
Definition: StdBuf.h:455
const char * getData() const
Definition: StdBuf.h:442
void Copy()
Definition: StdBuf.h:467
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
void Clear()
Definition: StdBuf.h:466
size_t getLength() const
Definition: StdBuf.h:445
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
@ Ico_Notify
Definition: C4Gui.h:642
void CopyValidated(const char *szFromVal)