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