void C4Network2Players::UpdateSavegameAssignments(C4ClientPlayerInfos *pNewInfo)
{
	// safety
	if (!pNewInfo) return;
	// check all joins of new info; backwards so they can be deleted
	C4PlayerInfo *pInfo, *pInfo2, *pSaveInfo; int i=pNewInfo->GetPlayerCount(), j, id;
	while (i--) if ((pInfo = pNewInfo->GetPlayerInfo(i)))
			if ((id=pInfo->GetAssociatedSavegamePlayerID()))
			{
				// check for non-existant savegame players
				if (!(pSaveInfo=Game.RestorePlayerInfos.GetPlayerInfoByID(id)))
				{
					pInfo->SetAssociatedSavegamePlayer(id=0);
					pNewInfo->SetUpdated();
				}
				// check for duplicates (can't really occur...)
				if (id)
				{
					j=i;
					while ((pInfo2 = pNewInfo->GetPlayerInfo(++j)))
						if (pInfo2->GetAssociatedSavegamePlayerID() == id)
						{
							// fix it by resetting the savegame info
							pInfo->SetAssociatedSavegamePlayer(id=0);
							pNewInfo->SetUpdated(); break;
						}
				}
				// check against all infos of other clients
				C4ClientPlayerInfos *pkClientInfo; int k=0;
				while ((pkClientInfo = rInfoList.GetIndexedInfo(k++)) && id)
				{
					// if it's not an add packet, don't check own client twice
					if (pkClientInfo->GetClientID() == pNewInfo->GetClientID() && !(pNewInfo->IsAddPacket()))
						continue;
					// check against all players
					j=0;
					while ((pInfo2 = pkClientInfo->GetPlayerInfo(j++)))
						if (pInfo2->GetAssociatedSavegamePlayerID() == id)
						{
							// fix it by resetting the savegame info
							pInfo->SetAssociatedSavegamePlayer(id=0);
							pNewInfo->SetUpdated(); break;
						}
				}
				// if the player joined just for the savegame assignment, and that failed, delete it
				if (!id && pInfo->IsJoinForSavegameOnly())
					pNewInfo->RemoveIndexedInfo(i);
				// prev info
			}
}
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;
}
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();
}