void C4RoundResultsPlayer::EvaluatePlayer(C4Player *pPlr) { assert(pPlr); // set fields by player iTotalPlayingTime = pPlr->TotalPlayingTime; if (pPlr->Evaluated) { iScoreNew = pPlr->TotalScore; iScoreOld = iScoreNew - pPlr->LastRound.FinalScore; } else { // player not evaluated (e.g., removed by disconnect): Old score known only iScoreOld = pPlr->TotalScore; } // load icon from player fctBigIcon.Clear(); if (pPlr->BigIcon.Surface) { fctBigIcon.Create(pPlr->BigIcon.Wdt, pPlr->BigIcon.Hgt); pPlr->BigIcon.Draw(fctBigIcon); } // progress data by player C4PlayerInfo *pInfo = pPlr->GetInfo(); if (pInfo) { sLeagueProgressData.Copy(pInfo->GetLeagueProgressData()); } }
int32_t C4Team::GetFirstUnjoinedPlayerID() const { // search for a player that does not have the join-flag set int32_t i=iPlayerCount, idPlr, *piPlr = piPlayers; C4PlayerInfo *pInfo; while (i--) if ((pInfo = Game.PlayerInfos.GetPlayerInfoByID(idPlr = *piPlr++))) if (!pInfo->HasJoinIssued()) return idPlr; // none found return 0; }
bool C4PlayerList::Remove(C4Player *pPlr, bool fDisconnect, bool fNoCalls) { if (!pPlr) return false; // inform script if (!fNoCalls) ::Game.GRBroadcast(PSF_RemovePlayer, &C4AulParSet(C4VInt(pPlr->Number), C4VInt(pPlr->Team))); // Transfer ownership of other objects to team members if (!fNoCalls) pPlr->NotifyOwnedObjects(); // NET2: update player info list if (pPlr->ID) { C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(pPlr->ID); if (pInfo) { pInfo->SetRemoved(); if (fDisconnect) pInfo->SetDisconnected(); } // if player wasn't evaluated, store round results anyway if (!pPlr->Evaluated) Game.RoundResults.EvaluatePlayer(pPlr); } C4Player *pPrev=First; while (pPrev && pPrev->Next!=pPlr) pPrev=pPrev->Next; if (pPrev) pPrev->Next=pPlr->Next; else First=pPlr->Next; // Remove eliminated crew if (!fNoCalls) pPlr->RemoveCrewObjects(); // Clear object info pointers pPlr->CrewInfoList.DetachFromObjects(); // Clear viewports ::Viewports.CloseViewport(pPlr->Number, fNoCalls); // Check fullscreen viewports FullScreen.ViewportCheck(); // Remove player delete pPlr; // Validate object owners ::Objects.ValidateOwners(); // Update console Console.UpdateMenus(); return true; }
void C4Team::AddPlayer(C4PlayerInfo &rInfo, bool fAdjustPlayer) { // must not happen! assert(rInfo.GetID()); if (!rInfo.GetID()) return; // add player; grow vector if necessary if (iPlayerCount >= iPlayerCapacity) { int32_t *piNewPlayers = new int32_t[iPlayerCapacity = (iPlayerCount+4)&~3]; if (iPlayerCount) memcpy(piNewPlayers, piPlayers, iPlayerCount*sizeof(int32_t)); delete [] piPlayers; piPlayers = piNewPlayers; } // store new player piPlayers[iPlayerCount++] = rInfo.GetID(); if (!fAdjustPlayer) return; // set values in info rInfo.SetTeam(GetID()); if (Game.Teams.IsTeamColors()) rInfo.SetColor(GetColor()); // and in actual player, if it is joined already if (rInfo.IsJoined()) { C4Player *pJoinedPlr = ::Players.GetByInfoID(rInfo.GetID()); assert(pJoinedPlr || (rInfo.GetType() == C4PT_Script)); if (pJoinedPlr) { pJoinedPlr->Team = GetID(); if (Game.Teams.IsTeamColors()) pJoinedPlr->SetPlayerColor(GetColor()); } } }
bool C4Team::HasWon() const { // return true if any member player of the team has won bool fHasWon = false; for (int32_t i=0; i<iPlayerCount; ++i) { int32_t id; C4PlayerInfo *pInfo; if ((id = piPlayers[i])) if ((pInfo = Game.PlayerInfos.GetPlayerInfoByID(id))) if (pInfo->HasWon()) { fHasWon = true; break; } } return fHasWon; }
void C4TeamList::ReassignAllTeams() { assert(::Control.isCtrlHost()); if (!::Control.isCtrlHost()) return; // go through all player infos; reset team in them int32_t idStart = -1; C4PlayerInfo *pNfo; while ((pNfo = Game.PlayerInfos.GetNextPlayerInfoByID(idStart))) { idStart = pNfo->GetID(); if (pNfo->HasJoinIssued()) continue; pNfo->SetTeam(0); // mark changed info as updated C4ClientPlayerInfos *pCltInfo = Game.PlayerInfos.GetClientInfoByPlayerID(idStart); assert(pCltInfo); if (pCltInfo) { pCltInfo->SetUpdated(); } } // clear players from team lists RecheckPlayers(); EnsureTeamCount(); // reassign them idStart = -1; while ((pNfo = Game.PlayerInfos.GetNextPlayerInfoByID(idStart))) { idStart = pNfo->GetID(); if (pNfo->HasJoinIssued()) continue; assert(!pNfo->GetTeam()); RecheckPlayerInfoTeams(*pNfo, true); } }
void C4Team::RecheckPlayers() { // check all players within the team for (int32_t i=0; i<iPlayerCount; ++i) { bool fIsValid = false; int32_t id; C4PlayerInfo *pInfo; if ((id = piPlayers[i])) if ((pInfo = Game.PlayerInfos.GetPlayerInfoByID(id))) if (pInfo->GetTeam() == GetID()) if (pInfo->IsUsingTeam()) fIsValid = true; // removal will decrease iPlayerCount, which will abort the loop earlier if (!fIsValid) RemoveIndexedPlayer(i--); } // now check for any new players in the team int32_t id = 0; C4PlayerInfo *pInfo; while ((pInfo = Game.PlayerInfos.GetNextPlayerInfoByID(id))) { id = pInfo->GetID(); if (pInfo->GetTeam() == GetID()) if (pInfo->IsUsingTeam()) if (!IsPlayerIDInTeam(id)) AddPlayer(*pInfo, false); } }
void C4GameSave::WriteDescPlayers(StdStrBuf &sBuf, bool fByTeam, int32_t idTeam) { // write out all players; only if they match the given team if specified C4PlayerInfo *pPlr; bool fAnyPlrWritten = false; for (int i = 0; (pPlr = Game.PlayerInfos.GetPlayerInfoByIndex(i)); i++) if (pPlr->HasJoined() && !pPlr->IsRemoved() && !pPlr->IsInvisible()) { if (fByTeam) { if (idTeam) { // match team if (pPlr->GetTeam() != idTeam) continue; } else { // must be in no known team if (Game.Teams.GetTeamByID(pPlr->GetTeam())) continue; } } if (fAnyPlrWritten) sBuf.Append(", "); else if (fByTeam && idTeam) { C4Team *pTeam = Game.Teams.GetTeamByID(idTeam); if (pTeam) sBuf.AppendFormat("%s: ", pTeam->GetName()); } sBuf.Append(pPlr->GetName()); fAnyPlrWritten = true; } if (fAnyPlrWritten) WriteDescLineFeed(sBuf); }
void C4PlayerInfoListAttributeConflictResolver::MarkConflicts(C4ClientPlayerInfos &rCheckPacket, bool fTestOriginal) { C4PlayerInfo *pCheckAgainstInfo; // check current and original attribute against all player infos for (int32_t j=0; (pCheckAgainstInfo = rCheckPacket.GetPlayerInfo(j)); ++j) { if (pCheckAgainstInfo->IsUsingAttribute(eAttr)) if (!pResolveInfo->GetID() || pResolveInfo->GetID() != pCheckAgainstInfo->GetID()) if (pResolveInfo != pCheckAgainstInfo) { // current conflict is marked only if the checked packet has same of lower priority than the one compared to // if the priority is higher, the attribute shall be changed in the other, low priority info instead! bool fHasHigherPrio = (GetAttributePriorityDifference(pResolveInfo, pResolvePacket, pCheckAgainstInfo, &rCheckPacket) > 0); if (!fHasHigherPrio) if (IsAttributeConflict(pCheckAgainstInfo, pResolveInfo, C4PlayerInfo::PLRAL_Current)) fCurrentConflict = true; if (fTestOriginal) { if (IsAttributeConflict(pCheckAgainstInfo, pResolveInfo, C4PlayerInfo::PLRAL_Original)) { if (fHasHigherPrio && !fOriginalConflict && !pLowPrioOriginalConflictPacket) { // original attribute is taken by a low prio packet - do not mark an original conflict, but remember the packet // that's blocking it pLowPrioOriginalConflictPacket = &rCheckPacket; } else { // original attribute is taken by either one higher/equal priority by packet, or by two low prio packets // in this case, don't revert to original pLowPrioOriginalConflictPacket = nullptr; fOriginalConflict = true; } } if (IsAttributeConflict(pCheckAgainstInfo, pResolveInfo, C4PlayerInfo::PLRAL_Alternate)) { if (fHasHigherPrio && !fAlternateConflict && !pLowPrioAlternateConflictPacket) pLowPrioAlternateConflictPacket = &rCheckPacket; else fAlternateConflict = true; } } } } }
DWORD C4Network2Players::GetClientChatColor(int idForClient, bool fLobby) const { // return color of first joined player; force to white for unknown // deactivated always white const C4Client *pClient = Game.Clients.getClientByID(idForClient); if (pClient && pClient->isActivated()) { // get players for activated C4ClientPlayerInfos *pInfoPacket = rInfoList.GetInfoByClientID(idForClient); C4PlayerInfo *pPlrInfo; if (pInfoPacket && (pPlrInfo = pInfoPacket->GetPlayerInfo(0, C4PT_User))) { if (fLobby) return pPlrInfo->GetLobbyColor(); else return pPlrInfo->GetColor(); } } // default color return 0xffffff; }
bool C4PlayerList::Save(C4Group &hGroup, bool fStoreTiny, const C4PlayerInfoList &rStoreList) { StdStrBuf sTempFilename; bool fSuccess = true; // Save to external player files and add to group for (C4Player *pPlr=First; pPlr; pPlr=pPlr->Next) { // save only those in the list, and only those with a filename C4PlayerInfo *pNfo = rStoreList.GetPlayerInfoByID(pPlr->ID); if (!pNfo) continue; if (!pNfo->GetFilename() || !*pNfo->GetFilename()) continue;; // save over original file? bool fStoreOnOriginal = (!fStoreTiny && pNfo->GetType() == C4PT_User); // Create temporary file sTempFilename.Copy(Config.AtTempPath(pNfo->GetFilename())); if (fStoreOnOriginal) if (!C4Group_CopyItem(pPlr->Filename, sTempFilename.getData())) return false; // Open group C4Group PlrGroup; if (!PlrGroup.Open(sTempFilename.getData(), !fStoreOnOriginal)) return false; // Save player if (!pPlr->Save(PlrGroup, true, fStoreOnOriginal)) return false; PlrGroup.Close(); // Add temp file to group if (!hGroup.Move(sTempFilename.getData(), pNfo->GetFilename())) return false; } return fSuccess; }
void C4PlayerInfoListAttributeConflictResolver::ResolveInPacket() { // check all player infos fAnyChange = false; int32_t iCheck = 0; while ((pResolveInfo = pResolvePacket->GetPlayerInfo(iCheck++))) { // not already joined? Joined player must not change their attributes! if (pResolveInfo->HasJoined()) continue; DWORD dwPrevColor = pResolveInfo->GetColor(); StdStrBuf sPrevForcedName; sPrevForcedName.Copy(pResolveInfo->GetForcedName()); // check attributes: Name and color for (eAttr = C4PlayerInfo::PLRATT_Color; eAttr != C4PlayerInfo::PLRATT_Last; eAttr = (C4PlayerInfo::Attribute) (eAttr+1)) { if (eAttr == C4PlayerInfo::PLRATT_Color) { // no color change in savegame associations if (pResolveInfo->GetAssociatedSavegamePlayerID()) continue; // or forced team colors if (Game.Teams.IsTeamColors() && Game.Teams.GetTeamByID(pResolveInfo->GetTeam())) continue; } else if (eAttr == C4PlayerInfo::PLRATT_Name) { // no name change if a league name is used if (pResolveInfo->getLeagueAccount() && *pResolveInfo->getLeagueAccount()) continue; } // not if attributes are otherwise fixed (e.g., for script players) if (pResolveInfo->IsAttributesFixed()) continue; // resolve in this info ResolveInInfo(); } // mark change for return value if anything was changed if (pResolveInfo->GetColor() != dwPrevColor || (pResolveInfo->GetForcedName() != sPrevForcedName)) fAnyChange = true; // next player info check } // mark update if anything was changed if (fAnyChange) pResolvePacket->SetUpdated(); }
void C4Network2Players::OnClientPart(C4Client *pPartClient) { // lobby could be notified about the removal - but this would be redundant, because // client leave notification is already done directly; this will delete any associated players C4ClientPlayerInfos **ppCltInfo = rInfoList.GetInfoPtrByClientID(pPartClient->getID()); // abort here if no info is registered - client seems to have had a short life only, anyway... if (!ppCltInfo) return; // remove all unjoined player infos for (int32_t i = 0; i < (*ppCltInfo)->GetPlayerCount();) { C4PlayerInfo *pInfo = (*ppCltInfo)->GetPlayerInfo(i); // not joined yet? remove it if (!pInfo->HasJoined()) (*ppCltInfo)->RemoveIndexedInfo(i); else // just ignore, the "removed" flag will be set eventually i++; } // empty? remove if (!(*ppCltInfo)->GetPlayerCount()) rInfoList.RemoveInfo(ppCltInfo); // update team association to left player Game.Teams.RecheckPlayers(); // host: update player data according to leaver if (::Network.isHost() && ::Network.isEnabled()) { // host: update any player colors and names rInfoList.UpdatePlayerAttributes(); // team distribution of remaining unjoined players may change Game.Teams.RecheckTeams(); // league score gains may now be different Game.PlayerInfos.ResetLeagueProjectedGain(true); // send changes to all clients and reset update flags SendUpdatedPlayers(); } // invalidate reference if (::Network.isHost()) ::Network.InvalidateReference(); }
StdStrBuf C4Team::GetNameWithParticipants() const { // compose team name like "Team 1 (boni, GhostBear, Clonko)" // or just "Team 1" for empty team StdStrBuf sTeamName; sTeamName.Copy(GetName()); if (GetPlayerCount()) { sTeamName.Append(" ("); int32_t iTeamPlrCount=0; for (int32_t j=0; j<GetPlayerCount(); ++j) { int32_t iPlr = GetIndexedPlayer(j); C4PlayerInfo *pPlrInfo; if (iPlr) if ((pPlrInfo = Game.PlayerInfos.GetPlayerInfoByID(iPlr))) { if (iTeamPlrCount++) sTeamName.Append(", "); sTeamName.Append(pPlrInfo->GetName()); } } sTeamName.AppendChar(')'); } return sTeamName; }
void C4Network2Players::JoinUnjoinedPlayersInControlQueue(C4ClientPlayerInfos *pNewPacket) { // only host may join any players to the queue assert(::Network.isHost()); // check all players int i=0; C4PlayerInfo *pInfo; while ((pInfo = pNewPacket->GetPlayerInfo(i++))) // not yet joined and no savegame assignment? if (!pInfo->HasJoinIssued()) if (!pInfo->GetAssociatedSavegamePlayerID()) { // join will be marked when queue is executed (C4Player::Join) // but better mark join now already to prevent permanent sending overkill pInfo->SetJoinIssued(); // do so! C4Network2Res *pPlrRes = pInfo->GetRes(); C4Network2Client *pClient = ::Network.Clients.GetClientByID(pNewPacket->GetClientID()); if (!pPlrRes || (!pClient && pNewPacket->GetClientID() != ::Control.ClientID())) if (pInfo->GetType() != C4PT_Script) { // failure: Non-script players must have a res to join from! const char *szPlrName = pInfo->GetName(); if (!szPlrName) szPlrName="???"; LogF("Network: C4Network2Players::JoinUnjoinedPlayersInControlQueue failed to join player %s!", szPlrName); continue; } if (pPlrRes) { // join with resource Game.Input.Add(CID_JoinPlr, new C4ControlJoinPlayer(pPlrRes->getFile(), pNewPacket->GetClientID(), pInfo->GetID(), pPlrRes->getCore())); } else { // join without resource (script player) Game.Input.Add(CID_JoinPlr, new C4ControlJoinPlayer(NULL, pNewPacket->GetClientID(), pInfo->GetID())); } } }
void C4TeamList::ReassignAllTeams() { assert(::Control.isCtrlHost()); if (!::Control.isCtrlHost()) return; // go through all player infos; reset team in them int32_t idStart = -1; C4PlayerInfo *pNfo; while ((pNfo = Game.PlayerInfos.GetNextPlayerInfoByID(idStart))) { idStart = pNfo->GetID(); if (pNfo->HasJoinIssued()) continue; pNfo->SetTeam(0); // mark changed info as updated C4ClientPlayerInfos *pCltInfo = Game.PlayerInfos.GetClientInfoByPlayerID(idStart); assert(pCltInfo); if (pCltInfo) { pCltInfo->SetUpdated(); } } // clear players from team lists RecheckPlayers(); // in random autogenerate mode, there must be exactly two teams if (IsRandomTeam()) if (IsAutoGenerateTeams() && GetTeamCount() != 2) { ClearTeams(); GenerateDefaultTeams(2); } // reassign them idStart = -1; while ((pNfo = Game.PlayerInfos.GetNextPlayerInfoByID(idStart))) { idStart = pNfo->GetID(); if (pNfo->HasJoinIssued()) continue; assert(!pNfo->GetTeam()); RecheckPlayerInfoTeams(*pNfo, true); } }
bool C4TeamList::RecheckPlayerInfoTeams(C4PlayerInfo &rNewJoin, bool fByHost) { // only if enabled assert(IsMultiTeams()); if (!IsMultiTeams()) return false; // check whether a new team is to be assigned first C4Team *pCurrentTeam = GetTeamByPlayerID(rNewJoin.GetID()); int32_t idCurrentTeam = pCurrentTeam ? pCurrentTeam->GetID() : 0; if (rNewJoin.GetTeam()) { // was that team a change to the current team? // no change anyway: OK, skip this info if (idCurrentTeam == rNewJoin.GetTeam()) return true; // the player had a different team assigned: Check if changes are allowed at all if (eTeamDist == TEAMDIST_Free || (eTeamDist == TEAMDIST_Host && fByHost)) // also make sure that selecting this team is allowed, e.g. doesn't break the team limit // this also checks whether the team number is a valid team - but it would accept TEAMID_New, which shouldn't be used in player infos! if (rNewJoin.GetTeam() != TEAMID_New && IsJoin2TeamAllowed(rNewJoin.GetTeam())) // okay; accept change return true; // Reject change by reassigning the current team rNewJoin.SetTeam(idCurrentTeam); // and determine a new team, if none has been assigned yet if (idCurrentTeam) return true; } // new team assignment // teams are always needed in the lobby, so there's a team preset to change // for runtime joins, teams are needed if specified by teams.txt or if any teams have been created before (to avoid mixed team-noteam-scenarios) // but only assign teams in runtime join if the player won't pick it himself bool fWillHaveLobby = ::Network.isEnabled() && !::Network.Status.isPastLobby() && Game.fLobby; bool fHasOrWillHaveLobby = ::Network.isLobbyActive() || fWillHaveLobby; bool fCanPickTeamAtRuntime = !IsRandomTeam() && (rNewJoin.GetType() == C4PT_User) && IsRuntimeJoinTeamChoice(); bool fIsTeamNeeded = IsRuntimeJoinTeamChoice() || GetTeamCount(); if (!fHasOrWillHaveLobby && (!fIsTeamNeeded || fCanPickTeamAtRuntime)) return false; // get least-used team C4Team *pAssignTeam=NULL; C4Team *pLowestTeam = GetRandomSmallestTeam(); // melee mode if (IsAutoGenerateTeams() && !IsRandomTeam()) { // reuse old team only if it's empty if (pLowestTeam && !pLowestTeam->GetPlayerCount()) pAssignTeam = pLowestTeam; else { // no empty team: generate new GenerateDefaultTeams(iLastTeamID+1); pAssignTeam = GetTeamByID(iLastTeamID); } } else { if (!pLowestTeam) { // not enough teams defined in teamwork mode? // then create two teams as default if (!GetTeamByIndex(1)) GenerateDefaultTeams(2); else // otherwise, all defined teams are full. This is a scenario error, because MaxPlayer should have been adjusted return false; pLowestTeam = GetTeamByIndex(0); } pAssignTeam = pLowestTeam; } // assign it if (!pAssignTeam) return false; pAssignTeam->AddPlayer(rNewJoin, true); return true; }
void C4Network2Players::HandlePlayerInfoUpdRequest(const class C4ClientPlayerInfos *pInfoPacket, bool fByHost) { // network host only assert(::Network.isEnabled()); assert(::Network.isHost()); // copy client infos (need to be adjusted) C4ClientPlayerInfos OwnInfoPacket(*pInfoPacket); // safety: check any duplicate, unjoined players first // check those with unassigned IDs only, so update packets won't be rejected by this if (!OwnInfoPacket.IsInitialPacket()) { C4ClientPlayerInfos *pExistingClientInfo = rInfoList.GetInfoByClientID(OwnInfoPacket.GetClientID()); if (pExistingClientInfo) { int iCnt=OwnInfoPacket.GetPlayerCount(); C4PlayerInfo *pPlrInfo; C4Network2Res *pRes; while (iCnt--) if ((pPlrInfo=OwnInfoPacket.GetPlayerInfo(iCnt))) if (!pPlrInfo->GetID()) if ((pRes = pPlrInfo->GetRes())) if (pExistingClientInfo->GetPlayerInfoByRes(pRes->getResID())) { // double join: simply deny without message #ifdef _DEBUG Log("Network: Duplicate player join rejected!"); #endif OwnInfoPacket.RemoveIndexedInfo(iCnt); } } if (!OwnInfoPacket.GetPlayerCount()) { // player join request without players: probably all removed because doubled #ifdef _DEBUG Log("Network: Empty player join request ignored!"); #endif return; } } // assign player IDs if (!rInfoList.AssignPlayerIDs(&OwnInfoPacket) && OwnInfoPacket.IsAddPacket()) { // no players could be joined in an add request: probably because the maximum player limit has been reached return; } // check doubled savegame player usage UpdateSavegameAssignments(&OwnInfoPacket); // update teams rInfoList.AssignTeams(&OwnInfoPacket, fByHost); // update any other player colors and names // this may only change colors and names of all unjoined players (which is all players in lobby mode) // any affected players will get an updated-flag rInfoList.UpdatePlayerAttributes(&OwnInfoPacket, true); // league score gains may now be different rInfoList.ResetLeagueProjectedGain(true); int32_t iPlrInfo = 0; C4PlayerInfo *pPlrInfo; while ((pPlrInfo = OwnInfoPacket.GetPlayerInfo(iPlrInfo++))) pPlrInfo->ResetLeagueProjectedGain(); if (Game.Parameters.isLeague()) { // lobby only if (!::Network.isLobbyActive()) return; // check league authentication for new players for (int i = 0; i < OwnInfoPacket.GetPlayerCount(); i++) if (!rInfoList.GetPlayerInfoByID(OwnInfoPacket.GetPlayerInfo(i)->GetID())) { C4PlayerInfo *pInfo = OwnInfoPacket.GetPlayerInfo(i); // remove player infos without authentication if (!::Network.LeaguePlrAuthCheck(pInfo)) { OwnInfoPacket.RemoveIndexedInfo(i); i--; } else // always reset authentication ID after check - it's not needed anymore pInfo->SetAuthID(""); } } // send updates to all other clients and reset update flags SendUpdatedPlayers(); // finally, add new player join as direct input // this will add the player infos directly on host side (DirectExec as a subcall), // so future player join request will take the other joined clients into consideration // when assigning player colors, etc.; it will also start resource loading // in running mode, this call will also put the actual player joins into the queue ::Control.DoInput(CID_PlrInfo, new C4ControlPlayerInfo(OwnInfoPacket), CDT_Direct); // notify lobby of updates C4GameLobby::MainDlg *pLobby = ::Network.GetLobby(); if (pLobby) pLobby->OnPlayersChange(); }
void C4PlayerInfoListAttributeConflictResolver::ResolveInInfo() { // trial-loop for assignment of new player colors/names int32_t iTries = 0; // original/alternate conflict evaluated once only fOriginalConflict = false; fAlternateConflict = (eAttr == C4PlayerInfo::PLRATT_Name) || !pResolveInfo->GetAlternateColor(); // mark as conflict if there is no alternate color/name for (;;) { // check against all other player infos, and given info, too (may be redundant) fCurrentConflict = false; pLowPrioOriginalConflictPacket = pLowPrioAlternateConflictPacket = nullptr; MarkConflicts(rPriCheckList, !iTries); // check secondary list, too. But only for colors, not for names, because secondary list is Restore list // and colors are retained in restore while names are always taken from new joins if (eAttr != C4PlayerInfo::PLRATT_Name) MarkConflicts(rSecCheckList, !iTries); // and mark conflicts in additional packet that' sbeen passed if (pSecPacket) MarkConflicts(*pSecPacket, !iTries); // color conflict resolving if (eAttr == C4PlayerInfo::PLRATT_Color) { // original color free but not used? if (!iTries) { if (pResolveInfo->GetColor() != pResolveInfo->GetOriginalColor()) { if (!fOriginalConflict) { // revert to original color! pResolveInfo->SetColor(pResolveInfo->GetOriginalColor()); // in case a lower priority packet was blocking the attribute, re-check that packet // note that the may readd the current resolve packet, but the conflict will occur with the // lower priority packet in the next loop if (pLowPrioOriginalConflictPacket) ReaddInfoForCheck(pLowPrioOriginalConflictPacket); // done with this player (breaking the trial-loop) break; } // neither original nor alternate color used but alternate color free? else if (pResolveInfo->GetColor() != pResolveInfo->GetAlternateColor() && !fAlternateConflict) { // revert to alternate pResolveInfo->SetColor(pResolveInfo->GetAlternateColor()); if (pLowPrioAlternateConflictPacket) ReaddInfoForCheck(pLowPrioAlternateConflictPacket); // done with this player (breaking the trial-loop) break; } } } // conflict found? if (!fCurrentConflict) // done with this player, then - break the trial-loop break; // try to get a new, unused player color uint32_t dwNewClr; if (++iTries > C4MaxPlayerColorChangeTries) { LogF(LoadResStr("IDS_PRC_NOREPLPLRCLR"), pResolveInfo->GetName() ? pResolveInfo->GetName() : "<NONAME>"); // since there's a conflict anyway, change to original pResolveInfo->SetColor(pResolveInfo->GetOriginalColor()); break; } else dwNewClr = GenerateRandomPlayerColor(iTries); pResolveInfo->SetColor(dwNewClr); } else // if (eAttr == PLRATT_Name) { // name conflict resolving // original name free but not used? if (!SEqualNoCase(pResolveInfo->GetName(), pResolveInfo->GetOriginalName())) if (!fOriginalConflict) { // revert to original name! pResolveInfo->SetForcedName(nullptr); if (pLowPrioOriginalConflictPacket) ReaddInfoForCheck(pLowPrioOriginalConflictPacket); // done with this player (breaking the trial-loop) break; } // conflict found? if (!fCurrentConflict) // done with this player, then - break the trial-loop break; // generate new name by appending an index if (++iTries > C4MaxPlayerNameChangeTries) break; pResolveInfo->SetForcedName(FormatString("%s (%d)", pResolveInfo->GetOriginalName(), iTries+1).getData()); } } }