예제 #1
0
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 C4Network2Players::HandlePlayerInfo(const class C4ClientPlayerInfos &rInfoPacket)
{
	// network only
	assert(::Network.isEnabled());
	// copy client player infos out of packet to be used in local list
	C4ClientPlayerInfos *pClientInfo = new C4ClientPlayerInfos(rInfoPacket);
	// add client info  to local player info list - eventually deleting pClientInfo and
	// returning a pointer to the new info structure when multiple player infos are merged
	// may also replace existing info, if this is an update-call
	pClientInfo = rInfoList.AddInfo(pClientInfo);
	// make sure team list reflects teams set in player infos
	Game.Teams.RecheckPlayers();
	Game.Teams.RecheckTeams(); // recheck random teams - if a player left, teams may need to be rebalanced
	// make sure resources are loaded for those players
	rInfoList.LoadResources();
	// get associated client - note that pClientInfo might be NULL for empty packets that got discarded
	if (pClientInfo)
	{
		const C4Client *pClient = Game.Clients.getClientByID(pClientInfo->GetClientID());
		// host, game running and client active already?
		if (::Network.isHost() && ::Network.isRunning() && pClient && pClient->isActivated())
		{
			// then join the players immediately
			JoinUnjoinedPlayersInControlQueue(pClientInfo);
		}
	}
	// adding the player may have invalidated other players (through team settings). Send them.
	SendUpdatedPlayers();
	// lobby: update players
	C4GameLobby::MainDlg *pLobby = ::Network.GetLobby();
	if (pLobby) pLobby->OnPlayersChange();
	// invalidate reference
	::Network.InvalidateReference();
}
C4ClientPlayerInfos *C4Network2Players::GetLocalPlayerInfoPacket() const
{
	// get local client ID
	int iLocalClientID = Game.Clients.getLocalID();
	// check all packets for same client ID as local
	int i=0; C4ClientPlayerInfos *pkInfo;
	while ((pkInfo = rInfoList.GetIndexedInfo(i++)))
		if (pkInfo->GetClientID() == iLocalClientID)
			// found
			return pkInfo;
	// not found
	return NULL;
}
void C4Network2Players::SendUpdatedPlayers()
{
	// check all clients for update
	C4ClientPlayerInfos *pUpdInfo; int i=0;
	while ((pUpdInfo = rInfoList.GetIndexedInfo(i++)))
		if (pUpdInfo->IsUpdated())
		{
			pUpdInfo->ResetUpdated();
			C4ControlPlayerInfo *pkSend = new C4ControlPlayerInfo(*pUpdInfo);
			// send info to all
			::Control.DoInput(CID_PlrInfo, pkSend, CDT_Direct);
		}
}
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
			}
}
예제 #6
0
void C4TeamList::RecheckTeams()
{
	// automatic team distributions only
	if (!IsRandomTeam()) return;
	// host decides random teams
	if (!::Control.isCtrlHost())
	{
		// Still make sure that we have the right number of teams on the clients as well so that
		// GetTeamCount() does not report inconsistent values.
		EnsureTeamCount();
		return;
	}
	// random teams in auto generate mode? Make sure there are exactly two teams
	if (IsAutoGenerateTeams() && GetTeamCount() != 2)
		{
		ReassignAllTeams();
		return;
		}
	// redistribute players of largest team that has relocatable players left towards smaller teams
	for (;;)
	{
		C4Team *pLowestTeam = GetRandomSmallestTeam();
		if (!pLowestTeam) break; // no teams: Nothing to re-distribute.
		// get largest team that has relocateable players
		C4Team *pLargestTeam = nullptr;
		C4Team **ppCheck=ppList; int32_t iCnt=iTeamCount;
		for (; iCnt--; ++ppCheck) if (!pLargestTeam || pLargestTeam->GetPlayerCount() > (*ppCheck)->GetPlayerCount())
				if ((*ppCheck)->GetFirstUnjoinedPlayerID())
					pLargestTeam = *ppCheck;
		// no team can redistribute?
		if (!pLargestTeam) break;
		// redistribution won't help much?
		if (pLargestTeam->GetPlayerCount() - pLowestTeam->GetPlayerCount() <= 1) break;
		// okay; redistribute one player!
		int32_t idRedistPlayer = pLargestTeam->GetFirstUnjoinedPlayerID();
		C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(idRedistPlayer);
		assert(pInfo);
		if (!pInfo) break; // umn...serious problems
		pLargestTeam->RemovePlayerByID(idRedistPlayer);
		pLowestTeam->AddPlayer(*pInfo, true);
		C4ClientPlayerInfos *pClrInfo = Game.PlayerInfos.GetClientInfoByPlayerID(idRedistPlayer);
		assert(pClrInfo);
		// player info change: mark updated to remote clients get information
		if (pClrInfo)
		{
			pClrInfo->SetUpdated();
		}
	}
}
void C4Network2Players::OnStatusGoReached()
{
	// host only
	if (!::Network.isHost()) return;
	// check all player lists
	int i=0; C4ClientPlayerInfos *pkInfo;
	while ((pkInfo = rInfoList.GetIndexedInfo(i++)))
		// any unsent player joins?
		if (pkInfo->HasUnjoinedPlayers())
		{
			// get client core
			const C4Client *pClient = Game.Clients.getClientByID(pkInfo->GetClientID());
			// don't send if client is unknown or not activated yet
			if (!pClient || !pClient->isActivated()) continue;
			// send them w/o info packet
			// info packets are synced during pause mode
			JoinUnjoinedPlayersInControlQueue(pkInfo);
		}
}
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;
}
예제 #9
0
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();
}
예제 #10
0
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);
	}
}
예제 #11
0
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;
						}
					}
				}
	}
}
void C4Network2Players::ResetUpdatedPlayers()
{
	// mark all client packets as up-to-date
	C4ClientPlayerInfos *pUpdInfo; int i=0;
	while ((pUpdInfo = rInfoList.GetIndexedInfo(i++))) pUpdInfo->ResetUpdated();
}
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();
}