OpenClonk
C4Network2Players.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2004-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // NET2 network player management
17 // see header for some additional information
18 // 2do: Handle client joins after game go but before runtime join (Frame 0)?
19 // Those will not receive a player info list right in time
20 
21 #include "C4Include.h"
23 
24 #include "control/C4Control.h"
25 #include "control/C4GameControl.h"
26 #include "control/C4PlayerInfo.h"
27 #include "control/C4RoundResults.h"
28 #include "gui/C4GameLobby.h"
29 #include "network/C4Network2.h"
30 
31 // *** C4Network2Players
32 
33 C4Network2Players::C4Network2Players() : rInfoList(Game.Parameters.PlayerInfos)
34 {
35  // ctor - init rInfoList-ref to only
36 }
37 
39 {
40  // caution: In this call, only local players are joined
41  // remote players may have been added already for runtime joins
42  // not in replay
43  if (Game.C4S.Head.Replay) return;
44  // network only
45  assert(::Network.isEnabled());
46  // must init before game is running
47  assert(!Game.IsRunning);
48  if (::Network.isHost())
49  {
50  // host: Rejoin script players from savegame before joining local players so team distribution is done correctly
51  // But prepare empty host list before recreation
52  JoinLocalPlayer("", true);
55  }
56  else
57  {
58  // Client: join the local player(s)
60  }
61 }
62 
64 {
65  // nothing...
66 }
67 
68 bool C4Network2Players::JoinLocalPlayer(const char *szLocalPlayerFilename, bool initial)
69 {
70  // ignore in replay
71  // shouldn't even come here though
72  assert(!Game.C4S.Head.Replay);
73  if (Game.C4S.Head.Replay) return false;
74  // if observing: don't try
75  if (Game.Clients.getLocal()->isObserver()) return false;
76  // network only
77  assert(::Network.isEnabled());
78  // create join info packet
79  C4ClientPlayerInfos JoinInfo(szLocalPlayerFilename, !initial);
80  // league game: get authentication for players
81  if (Game.Parameters.isLeague())
82  for (int i = 0; i < JoinInfo.GetPlayerCount(); i++)
83  {
84  C4PlayerInfo *pInfo = JoinInfo.GetPlayerInfo(i);
85  if (!::Network.LeaguePlrAuth(pInfo))
86  {
87  JoinInfo.RemoveIndexedInfo(i);
88  i--;
89  }
90  }
91  // host or client?
92  if (::Network.isHost())
93  {
94  // error joining players? Zero players is OK for initial packet; marks host as observer
95  if (!initial && !JoinInfo.GetPlayerCount()) return false;
96  // handle it as a direct request
97  HandlePlayerInfoUpdRequest(&JoinInfo, true);
98  }
99  else
100  {
101  // clients request initial joins at host only
102  // create player info for local player joins
103  C4PacketPlayerInfoUpdRequest JoinRequest(JoinInfo);
104  // any players to join? Zero players is OK for initial packet; marks client as observer
105  // it's also necessary to send the empty player info packet, so the host will answer
106  // with infos of all other clients
107  if (!initial && !JoinRequest.Info.GetPlayerCount()) return false;
109  // request activation
110  if (JoinRequest.Info.GetPlayerCount() && !Game.Clients.getLocal()->isActivated())
112  }
113  // done, success
114  return true;
115 }
116 
118 {
119  // network only
120  assert(::Network.isEnabled());
121  // host or client?
122  if (::Network.isHost())
123  {
124  // host processes directly
125  HandlePlayerInfoUpdRequest(&rRequest, true);
126  }
127  else
128  {
129  // client sends request to host
130  C4PacketPlayerInfoUpdRequest UpdateRequest(rRequest);
132  }
133 }
134 
135 void C4Network2Players::HandlePlayerInfoUpdRequest(const class C4ClientPlayerInfos *pInfoPacket, bool fByHost)
136 {
137  // network host only
138  assert(::Network.isEnabled());
139  assert(::Network.isHost());
140  // copy client infos (need to be adjusted)
141  C4ClientPlayerInfos OwnInfoPacket(*pInfoPacket);
142  // safety: check any duplicate, unjoined players first
143  // check those with unassigned IDs only, so update packets won't be rejected by this
144  if (!OwnInfoPacket.IsInitialPacket())
145  {
146  C4ClientPlayerInfos *pExistingClientInfo = rInfoList.GetInfoByClientID(OwnInfoPacket.GetClientID());
147  if (pExistingClientInfo)
148  {
149  int iCnt=OwnInfoPacket.GetPlayerCount(); C4PlayerInfo *pPlrInfo;
150  C4Network2Res *pRes;
151  while (iCnt--) if ((pPlrInfo=OwnInfoPacket.GetPlayerInfo(iCnt)))
152  if (!pPlrInfo->GetID()) if ((pRes = pPlrInfo->GetRes()))
153  if (pExistingClientInfo->GetPlayerInfoByRes(pRes->getResID()))
154  {
155  // double join: simply deny without message
156 #ifdef _DEBUG
157  Log("Network: Duplicate player join rejected!");
158 #endif
159  OwnInfoPacket.RemoveIndexedInfo(iCnt);
160  }
161  }
162  if (!OwnInfoPacket.GetPlayerCount())
163  {
164  // player join request without players: probably all removed because doubled
165 #ifdef _DEBUG
166  Log("Network: Empty player join request ignored!");
167 #endif
168  return;
169  }
170  }
171  // assign player IDs
172  if (!rInfoList.AssignPlayerIDs(&OwnInfoPacket) && OwnInfoPacket.IsAddPacket())
173  {
174  // no players could be joined in an add request: probably because the maximum player limit has been reached
175  return;
176  }
177  // check doubled savegame player usage
178  UpdateSavegameAssignments(&OwnInfoPacket);
179  // update teams
180  rInfoList.AssignTeams(&OwnInfoPacket, fByHost);
181  // update any other player colors and names
182  // this may only change colors and names of all unjoined players (which is all players in lobby mode)
183  // any affected players will get an updated-flag
184  rInfoList.UpdatePlayerAttributes(&OwnInfoPacket, true);
185  // league score gains may now be different
186  rInfoList.ResetLeagueProjectedGain(true);
187  int32_t iPlrInfo = 0;
188  C4PlayerInfo *pPlrInfo;
189  while ((pPlrInfo = OwnInfoPacket.GetPlayerInfo(iPlrInfo++))) pPlrInfo->ResetLeagueProjectedGain();
190  if (Game.Parameters.isLeague())
191  {
192  // check league authentication for new players
193  for (int i = 0; i < OwnInfoPacket.GetPlayerCount(); i++)
194  {
195  if (!rInfoList.GetPlayerInfoByID(OwnInfoPacket.GetPlayerInfo(i)->GetID()))
196  {
197  C4PlayerInfo *pInfo = OwnInfoPacket.GetPlayerInfo(i);
198  // remove normal (non-script) player infos without authentication or when not in the lobby
199  if (pInfo->GetType() != C4PT_Script && (!::Network.isLobbyActive() || !::Network.LeaguePlrAuthCheck(pInfo)))
200  {
201  OwnInfoPacket.RemoveIndexedInfo(i);
202  i--;
203  }
204  else
205  // always reset authentication ID after check - it's not needed anymore
206  pInfo->SetAuthID("");
207  }
208  }
209  }
210  // send updates to all other clients and reset update flags
212  // finally, add new player join as direct input
213  // this will add the player infos directly on host side (DirectExec as a subcall),
214  // so future player join request will take the other joined clients into consideration
215  // when assigning player colors, etc.; it will also start resource loading
216  // in running mode, this call will also put the actual player joins into the queue
218  // notify lobby of updates
220  if (pLobby) pLobby->OnPlayersChange();
221 }
222 
224 {
225  // network only
226  assert(::Network.isEnabled());
227  // copy client player infos out of packet to be used in local list
228  C4ClientPlayerInfos *pClientInfo = new C4ClientPlayerInfos(rInfoPacket);
229  // add client info to local player info list - eventually deleting pClientInfo and
230  // returning a pointer to the new info structure when multiple player infos are merged
231  // may also replace existing info, if this is an update-call
232  pClientInfo = rInfoList.AddInfo(pClientInfo);
233  // make sure team list reflects teams set in player infos
235  Game.Teams.RecheckTeams(); // recheck random teams - if a player left, teams may need to be rebalanced
236  // make sure resources are loaded for those players
237  rInfoList.LoadResources();
238  // get associated client - note that pClientInfo might be nullptr for empty packets that got discarded
239  if (pClientInfo)
240  {
241  const C4Client *pClient = Game.Clients.getClientByID(pClientInfo->GetClientID());
242  // host, game running and client active already?
243  if (::Network.isHost() && ::Network.isRunning() && pClient && pClient->isActivated())
244  {
245  // then join the players immediately
246  JoinUnjoinedPlayersInControlQueue(pClientInfo);
247  }
248  }
249  // adding the player may have invalidated other players (through team settings). Send them.
251  // lobby: update players
253  if (pLobby) pLobby->OnPlayersChange();
254  // invalidate reference
256 }
257 
259 {
260  // check all clients for update
261  C4ClientPlayerInfos *pUpdInfo; int i=0;
262  while ((pUpdInfo = rInfoList.GetIndexedInfo(i++)))
263  if (pUpdInfo->IsUpdated())
264  {
265  pUpdInfo->ResetUpdated();
266  C4ControlPlayerInfo *pkSend = new C4ControlPlayerInfo(*pUpdInfo);
267  // send info to all
269  }
270 }
271 
272 void C4Network2Players::UpdateSavegameAssignments(C4ClientPlayerInfos *pNewInfo)
273 {
274  // safety
275  if (!pNewInfo) return;
276  // check all joins of new info; backwards so they can be deleted
277  C4PlayerInfo *pInfo, *pInfo2, *pSaveInfo; int i=pNewInfo->GetPlayerCount(), j, id;
278  while (i--) if ((pInfo = pNewInfo->GetPlayerInfo(i)))
279  if ((id=pInfo->GetAssociatedSavegamePlayerID()))
280  {
281  // check for non-existant savegame players
282  if (!(pSaveInfo=Game.RestorePlayerInfos.GetPlayerInfoByID(id)))
283  {
284  pInfo->SetAssociatedSavegamePlayer(id=0);
285  pNewInfo->SetUpdated();
286  }
287  // check for duplicates (can't really occur...)
288  if (id)
289  {
290  j=i;
291  while ((pInfo2 = pNewInfo->GetPlayerInfo(++j)))
292  if (pInfo2->GetAssociatedSavegamePlayerID() == id)
293  {
294  // fix it by resetting the savegame info
295  pInfo->SetAssociatedSavegamePlayer(id=0);
296  pNewInfo->SetUpdated(); break;
297  }
298  }
299  // check against all infos of other clients
300  C4ClientPlayerInfos *pkClientInfo; int k=0;
301  while ((pkClientInfo = rInfoList.GetIndexedInfo(k++)) && id)
302  {
303  // if it's not an add packet, don't check own client twice
304  if (pkClientInfo->GetClientID() == pNewInfo->GetClientID() && !(pNewInfo->IsAddPacket()))
305  continue;
306  // check against all players
307  j=0;
308  while ((pInfo2 = pkClientInfo->GetPlayerInfo(j++)))
309  if (pInfo2->GetAssociatedSavegamePlayerID() == id)
310  {
311  // fix it by resetting the savegame info
312  pInfo->SetAssociatedSavegamePlayer(id=0);
313  pNewInfo->SetUpdated(); break;
314  }
315  }
316  // if the player joined just for the savegame assignment, and that failed, delete it
317  if (!id && pInfo->IsJoinForSavegameOnly())
318  pNewInfo->RemoveIndexedInfo(i);
319  // prev info
320  }
321 }
322 
324 {
325  // mark all client packets as up-to-date
326  C4ClientPlayerInfos *pUpdInfo; int i=0;
327  while ((pUpdInfo = rInfoList.GetIndexedInfo(i++))) pUpdInfo->ResetUpdated();
328 }
329 
330 void C4Network2Players::JoinUnjoinedPlayersInControlQueue(C4ClientPlayerInfos *pNewPacket)
331 {
332  // only host may join any players to the queue
333  assert(::Network.isHost());
334  // check all players
335  int i=0; C4PlayerInfo *pInfo;
336  while ((pInfo = pNewPacket->GetPlayerInfo(i++)))
337  // not yet joined and no savegame assignment?
338  if (!pInfo->HasJoinIssued()) if (!pInfo->GetAssociatedSavegamePlayerID())
339  {
340  // join will be marked when queue is executed (C4Player::Join)
341  // but better mark join now already to prevent permanent sending overkill
342  pInfo->SetJoinIssued();
343  // do so!
344  C4Network2Res *pPlrRes = pInfo->GetRes();
345  C4Network2Client *pClient = ::Network.Clients.GetClientByID(pNewPacket->GetClientID());
346  if (!pPlrRes || (!pClient && pNewPacket->GetClientID() != ::Control.ClientID()))
347  if (pInfo->GetType() != C4PT_Script)
348  {
349  // failure: Non-script players must have a res to join from!
350  const char *szPlrName = pInfo->GetName(); if (!szPlrName) szPlrName="???";
351  LogF("Network: C4Network2Players::JoinUnjoinedPlayersInControlQueue failed to join player %s!", szPlrName);
352  continue;
353  }
354  if (pPlrRes)
355  {
356  // join with resource
358  new C4ControlJoinPlayer(pPlrRes->getFile(), pNewPacket->GetClientID(), pInfo->GetID(), pPlrRes->getCore()));
359  }
360  else
361  {
362  // join without resource (script player)
364  new C4ControlJoinPlayer(nullptr, pNewPacket->GetClientID(), pInfo->GetID()));
365  }
366  }
367 }
368 
369 void C4Network2Players::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
370 {
371  if (!pConn) return;
372 
373  // find associated client
374  C4Network2Client *pClient = ::Network.Clients.GetClient(pConn);
375  if (!pClient) pClient = ::Network.Clients.GetClientByID(pConn->getClientID());
376 
377 #define GETPKT(type, name) \
378  assert(pPacket); const type &name = \
379  static_cast<const type &>(*pPacket);
380 
381  // player join request?
382  if (cStatus == PID_PlayerInfoUpdReq)
383  {
385  // this packet is sent to the host only, and thus cannot have been sent from the host
386  if (!::Network.isHost()) return;
387  // handle this packet
388  HandlePlayerInfoUpdRequest(&pkPlrInfo.Info, false);
389  }
390  else if (cStatus == PID_LeagueRoundResults)
391  {
392  GETPKT(C4PacketLeagueRoundResults, pkLeagueInfo);
393  // accepted from the host only
394  if (!pClient || !pClient->isHost()) return;
395  // process
396  Game.RoundResults.EvaluateLeague(pkLeagueInfo.sResultsString.getData(), pkLeagueInfo.fSuccess, pkLeagueInfo.Players);
397  }
398 
399 #undef GETPKT
400 }
401 
403 {
404  // lobby could be notified about the removal - but this would be redundant, because
405  // client leave notification is already done directly; this will delete any associated players
406  C4ClientPlayerInfos **ppCltInfo = rInfoList.GetInfoPtrByClientID(pPartClient->getID());
407  // abort here if no info is registered - client seems to have had a short life only, anyway...
408  if (!ppCltInfo) return;
409  // remove all unjoined player infos
410  for (int32_t i = 0; i < (*ppCltInfo)->GetPlayerCount();)
411  {
412  C4PlayerInfo *pInfo = (*ppCltInfo)->GetPlayerInfo(i);
413  // not joined yet? remove it
414  if (!pInfo->HasJoined())
415  (*ppCltInfo)->RemoveIndexedInfo(i);
416  else
417  // just ignore, the "removed" flag will be set eventually
418  i++;
419  }
420  // empty? remove
421  if (!(*ppCltInfo)->GetPlayerCount())
422  rInfoList.RemoveInfo(ppCltInfo);
423  // update team association to left player
425  // host: update player data according to leaver
426  if (::Network.isHost() && ::Network.isEnabled())
427  {
428  // host: update any player colors and names
429  rInfoList.UpdatePlayerAttributes();
430  // team distribution of remaining unjoined players may change
432  // league score gains may now be different
434  // send changes to all clients and reset update flags
436  }
437  // invalidate reference
438  if (::Network.isHost())
440 }
441 
443 {
444  // host only
445  if (!::Network.isHost()) return;
446  // check all player lists
447  int i=0; C4ClientPlayerInfos *pkInfo;
448  while ((pkInfo = rInfoList.GetIndexedInfo(i++)))
449  // any unsent player joins?
450  if (pkInfo->HasUnjoinedPlayers())
451  {
452  // get client core
453  const C4Client *pClient = Game.Clients.getClientByID(pkInfo->GetClientID());
454  // don't send if client is unknown or not activated yet
455  if (!pClient || !pClient->isActivated()) continue;
456  // send them w/o info packet
457  // info packets are synced during pause mode
458  JoinUnjoinedPlayersInControlQueue(pkInfo);
459  }
460 }
461 
463 {
464  // get local client ID
465  int iLocalClientID = Game.Clients.getLocalID();
466  // check all packets for same client ID as local
467  int i=0; C4ClientPlayerInfos *pkInfo;
468  while ((pkInfo = rInfoList.GetIndexedInfo(i++)))
469  if (pkInfo->GetClientID() == iLocalClientID)
470  // found
471  return pkInfo;
472  // not found
473  return nullptr;
474 }
475 
477 {
478  // just get from info list
479  return rInfoList.GetIndexedInfo(iIndex);
480 }
481 
482 DWORD C4Network2Players::GetClientChatColor(int idForClient, bool fLobby) const
483 {
484  // return color of first joined player; force to white for unknown
485  // deactivated always white
486  const C4Client *pClient = Game.Clients.getClientByID(idForClient);
487  if (pClient && pClient->isActivated())
488  {
489  // get players for activated
490  C4ClientPlayerInfos *pInfoPacket = rInfoList.GetInfoByClientID(idForClient);
491  C4PlayerInfo *pPlrInfo;
492  if (pInfoPacket && (pPlrInfo = pInfoPacket->GetPlayerInfo(0, C4PT_User)))
493  {
494  if (fLobby)
495  return pPlrInfo->GetLobbyColor();
496  else
497  return pPlrInfo->GetColor();
498  }
499  }
500  // default color
501  return 0xffffff;
502 }
503 
@ C4PT_User
Definition: C4Constants.h:154
@ C4PT_Script
Definition: C4Constants.h:155
C4GameControl Control
@ CDT_Direct
Definition: C4GameControl.h:36
C4Game Game
Definition: C4Globals.cpp:52
C4Network2 Network
Definition: C4Globals.cpp:53
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
#define GETPKT(type, name)
C4NetIOPacket MkC4NetIOPacket(char cStatus, const class C4PacketBase &Pkt, const C4NetIO::addr_t &addr=C4NetIO::addr_t())
Definition: C4PacketBase.h:40
@ CID_JoinPlr
Definition: C4PacketBase.h:159
@ CID_PlrInfo
Definition: C4PacketBase.h:158
@ PID_LeagueRoundResults
Definition: C4PacketBase.h:122
@ PID_PlayerInfoUpdReq
Definition: C4PacketBase.h:119
uint32_t DWORD
int iCnt
Definition: TstC4NetIO.cpp:32
int32_t getID() const
Definition: C4Client.h:105
bool isActivated() const
Definition: C4Client.h:110
bool isObserver() const
Definition: C4Client.h:111
C4Client * getLocal() const
Definition: C4Client.h:161
C4Client * getClientByID(int32_t iID) const
Definition: C4Client.cpp:200
int32_t getLocalID() const
Definition: C4Client.h:171
bool IsInitialPacket() const
Definition: C4PlayerInfo.h:261
int32_t GetClientID() const
Definition: C4PlayerInfo.h:257
void RemoveIndexedInfo(int32_t iAtIndex)
C4PlayerInfo * GetPlayerInfo(int32_t iIndex) const
int32_t GetPlayerCount() const
Definition: C4PlayerInfo.h:251
bool IsAddPacket() const
Definition: C4PlayerInfo.h:260
bool HasUnjoinedPlayers() const
C4PlayerInfo * GetPlayerInfoByRes(int32_t idResID) const
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
int32_t ClientID() const
C4RoundResults & RoundResults
Definition: C4Game.h:73
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
C4GameParameters & Parameters
Definition: C4Game.h:67
bool isLeague() const
bool isHost() const
C4Network2Client * GetClient(const char *szName) const
C4Network2Client * GetClientByID(int32_t iID) const
bool SendMsgToHost(C4NetIOPacket rPkt)
bool isRunning() const
Definition: C4Network2.h:206
class C4GameLobby::MainDlg * GetLobby() const
Definition: C4Network2.h:216
void InvalidateReference()
bool isEnabled() const
Definition: C4Network2.h:203
bool LeaguePlrAuthCheck(C4PlayerInfo *pInfo)
bool isHost() const
Definition: C4Network2.h:209
C4Network2ClientList Clients
Definition: C4Network2.h:116
bool isLobbyActive() const
Definition: C4Network2.h:204
void RequestActivate()
bool LeaguePlrAuth(C4PlayerInfo *pInfo)
C4ClientPlayerInfos * GetIndexedPlayerInfoPacket(int iIndex)
void OnClientPart(class C4Client *pPartClient)
DWORD GetClientChatColor(int idForClient, bool fLobby) const
void HandlePlayerInfo(const class C4ClientPlayerInfos &rInfoPacket)
void HandlePlayerInfoUpdRequest(const class C4ClientPlayerInfos *pInfoPacket, bool fByHost)
C4ClientPlayerInfos * GetLocalPlayerInfoPacket() const
bool JoinLocalPlayer(const char *szLocalPlayerFilename, bool initial=false)
void RequestPlayerInfoUpdate(const class C4ClientPlayerInfos &rRequest)
void HandlePacket(char cStatus, const C4PacketBase *pPacket, class C4Network2IOConnection *pConn)
const char * getFile() const
int32_t getResID() const
const C4Network2ResCore & getCore() const
C4ClientPlayerInfos Info
Definition: C4PlayerInfo.h:277
uint32_t GetColor() const
Definition: C4PlayerInfo.h:153
void SetJoinIssued()
Definition: C4PlayerInfo.h:112
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
int32_t GetAssociatedSavegamePlayerID() const
Definition: C4PlayerInfo.h:126
int32_t GetID() const
Definition: C4PlayerInfo.h:194
uint32_t GetLobbyColor() const
const char * GetName() const
Definition: C4PlayerInfo.h:157
void SetAuthID(const char *sznAuthID)
Definition: C4PlayerInfo.h:133
C4PlayerType GetType() const
Definition: C4PlayerInfo.h:152
C4Network2Res * GetRes() const
Definition: C4PlayerInfo.h:163
bool IsJoinForSavegameOnly()
Definition: C4PlayerInfo.h:130
C4ClientPlayerInfos * GetInfoByClientID(int32_t iClientID) const
Definition: C4PlayerInfo.h:361
void UpdatePlayerAttributes(C4ClientPlayerInfos *pForInfo, bool fResolveConflicts)
C4ClientPlayerInfos * GetIndexedInfo(int32_t iIndex) const
Definition: C4PlayerInfo.h:358
void AssignTeams(C4ClientPlayerInfos *pNewClientInfo, bool fByHost)
void CreateRestoreInfosForJoinedScriptPlayers(C4PlayerInfoList &rSavegamePlayers)
void RemoveInfo(C4ClientPlayerInfos **ppRemoveInfo)
Definition: C4PlayerInfo.h:386
C4PlayerInfo * GetPlayerInfoByID(int32_t id) const
C4ClientPlayerInfos * AddInfo(C4ClientPlayerInfos *pNewClientInfo)
C4ClientPlayerInfos ** GetInfoPtrByClientID(int32_t iClientID) const
void ResetLeagueProjectedGain(bool fSetUpdated)
bool AssignPlayerIDs(C4ClientPlayerInfos *pNewClientInfo)
void EvaluateLeague(const char *szResultMsg, bool fSuccess, const C4RoundResultsPlayers &rLeagueInfo)
bool Replay
Definition: C4Scenario.h:72
C4SHead Head
Definition: C4Scenario.h:232
void RecheckPlayers()
Definition: C4Teams.cpp:663
void RecheckTeams()
Definition: C4Teams.cpp:669