Files
GTASource/game/script/Handlers/GameScriptHandlerNetwork.cpp

2247 lines
74 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
//
// name: NetGameScriptHandler.cpp
// description: Project specific network script handler
// written by: John Gurney
//
#include "script/handlers/GameScriptHandlerNetwork.h"
// game includes
#include "event/EventGroup.h"
#include "event/EventNetwork.h"
#include "Network/Arrays/NetworkArrayMgr.h"
#include "Network/Events/NetworkEventTypes.h"
#include "Network/Network.h"
#include "Network/NetworkInterface.h"
#include "Network/Objects/Entities/NetObjGame.h"
#include "Network/Players/NetGamePlayer.h"
#include "Network/Sessions/NetworkSession.h"
#include "Peds/Ped.h"
#include "pickups/PickupManager.h"
#include "pickups/PickupPlacement.h"
#include "renderer/DrawLists/drawList.h"
#include "Scene/Physical.h"
#include "scene/world/GameWorld.h"
#include "Script/handlers/GameScriptEntity.h"
#include "Script/handlers/GameScriptResources.h"
#include "Script/gta_thread.h"
#include "script/script.h"
#include "script/script_channel.h"
#include "network/objects/NetworkObjectPopulationMgr.h"
// framework includes
#include "fwnet/netObjectMgrBase.h"
#include "fwnet/netTypes.h"
#include "fwscript/scripthandlermgr.h"
#include "fwscript/scriptinterface.h"
SCRIPT_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
// all handlers are network ones at the moment
FW_INSTANTIATE_CLASS_POOL(CGameScriptHandlerNetwork, scriptHandlerNetComponent::MAX_NUM_NET_COMPONENTS, atHashString("CGameScriptHandlerNetwork",0x4fe810e8));
FW_INSTANTIATE_CLASS_POOL(CGameScriptHandlerNetComponent, scriptHandlerNetComponent::MAX_NUM_NET_COMPONENTS, atHashString("CGameScriptHandlerNetComponent",0xed9e8fd3));
namespace rage
{
}
CGameScriptHandlerNetwork::CGameScriptHandlerNetwork(scrThread& scriptThread)
: CGameScriptHandler(scriptThread)
, m_nextFreeSequenceId(0)
, m_MaxNumParticipants(0)
, m_NumLocalReservedPeds(0)
, m_NumLocalReservedVehicles(0)
, m_NumLocalReservedObjects(0)
, m_NumGlobalReservedPeds(0)
, m_NumGlobalReservedVehicles(0)
, m_NumGlobalReservedObjects(0)
, m_NumCreatedPeds(0)
, m_NumCreatedVehicles(0)
, m_NumCreatedObjects(0)
, m_AcceptingPlayers(true)
, m_ActiveInSinglePlayer(false)
, m_InMPCutscene(false)
#if __DEV
, m_canRegisterMissionObjectCalled(false)
, m_canRegisterMissionPedCalled(false)
, m_canRegisterMissionVehicleCalled(false)
#endif
{
}
void CGameScriptHandlerNetwork::CreateNetworkComponent()
{
if (scriptVerify(!m_NetComponent))
{
m_NetComponent = rage_new CGameScriptHandlerNetComponent(*this, m_MaxNumParticipants);
}
}
void CGameScriptHandlerNetwork::DestroyNetworkComponent()
{
CGameScriptHandler::DestroyNetworkComponent();
if (m_Thread)
{
static_cast<GtaThread*>(m_Thread)->m_NetComponent = NULL;
}
}
bool CGameScriptHandlerNetwork::Update()
{
#if __DEV
// The can register debug flags need to be reset each frame for all active scripts
m_canRegisterMissionObjectCalled = 0;
m_canRegisterMissionPedCalled = 0;
m_canRegisterMissionVehicleCalled = 0;
#endif
return CGameScriptHandler::Update();
}
void CGameScriptHandlerNetwork::Shutdown()
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "Destroying handler 0x%x", this);
Displayf("Destroying handler for script %s (0x%p)\n", GetScriptId().GetLogName(), this);
return CGameScriptHandler::Shutdown();
}
bool CGameScriptHandlerNetwork::Terminate()
{
bool bRet = CGameScriptHandler::Terminate();
if (NetworkInterface::IsGameInProgress())
{
CGameScriptHandlerMgr::CRemoteScriptInfo* pInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(m_ScriptId, true);
if (pInfo)
{
pInfo->SetRunningLocally(false);
}
}
#if ENABLE_NETWORK_LOGGING
if (GetTotalNumReservedPeds() > 0 || GetTotalNumReservedVehicles() > 0 || GetTotalNumReservedObjects() > 0)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
#endif // ENABLE_NETWORK_LOGGING
return bRet;
}
bool CGameScriptHandlerNetwork::RegisterNewScriptObject(scriptHandlerObject &object, bool hostObject, bool network)
{
bool success = CGameScriptHandler::RegisterNewScriptObject(object, hostObject, network);
#if __ASSERT
if (network && GetNetworkComponent() && scriptVerify(object.GetScriptInfo()))
{
scriptVerifyf(!hostObject || GetNetworkComponent()->IsHostLocal() || object.HostObjectCanBeCreatedByClient(), "Script %s has created a host object when we are not the host of the script. The object will be removed on other machines", GetScriptName());
// the script id must have a timestamp to distinguish it from other scripts that have ran in the past
gnetAssert(static_cast<const CGameScriptId&>(GetScriptId()).GetTimeStamp() != 0);
scriptAssertf(GetNetworkComponent()->GetState() == scriptHandlerNetComponent::NETSCRIPT_PLAYING, "Script %s has not waited for NETWORK_GET_SCRIPT_STATUS to return a playing state before proceeding", GetScriptName());
}
#endif // __ASSERT
return success;
}
void CGameScriptHandlerNetwork::UnregisterScriptObject(scriptHandlerObject &object)
{
netObject* netObject = object.GetNetObject();
bool bHostObject = object.IsHostObject();
bool bIsPed = false;
bool bIsVeh = false;
bool bIsObj = false;
if (bHostObject && netObject && netObject->GetEntity())
{
if (netObject->GetEntity()->GetIsTypePed())
{
bIsPed = true;
}
else if (netObject->GetEntity()->GetIsTypeVehicle())
{
bIsVeh = true;
}
else if (netObject->GetEntity()->GetIsTypeObject())
{
// ignore doors
if (!static_cast<CObject*>(netObject->GetEntity())->IsADoor())
{
bIsObj = true;
}
}
else
{
gnetAssertf(0, "Unexpected target object type encountered!");
}
}
CGameScriptHandler::UnregisterScriptObject(object);
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if (bIsPed)
{
if (m_NumCreatedPeds > 0)
{
m_NumCreatedPeds--;
}
if (log)
{
log->WriteDataValue("Ped Reservation", "%d/%d (%d)", m_NumCreatedPeds, GetTotalNumReservedPeds(), m_NumLocalReservedPeds);
}
}
else if (bIsVeh)
{
if (m_NumCreatedVehicles > 0)
{
m_NumCreatedVehicles--;
}
if (log)
{
log->WriteDataValue("Vehicle Reservation", "%d/%d (%d)", m_NumCreatedVehicles, GetTotalNumReservedVehicles(), m_NumLocalReservedVehicles);
}
}
else if (bIsObj)
{
if (m_NumCreatedObjects > 0)
{
m_NumCreatedObjects--;
}
if (log)
{
log->WriteDataValue("Object Reservation", "%d/%d (%d)", m_NumCreatedObjects, GetTotalNumReservedObjects(), m_NumLocalReservedObjects);
}
}
}
ScriptResourceId CGameScriptHandlerNetwork::GetNextFreeResourceId(scriptResource& resource)
{
ScriptResourceId freeId = 0;
// sequences have their own id system, so that the ids match across the network when the order of sequences is defined in the same
// order by the script, irrespective of other resource allocations.
if (NetworkInterface::IsGameInProgress() && resource.GetType() == CGameScriptResource::SCRIPT_RESOURCE_SEQUENCE_TASK)
{
// check that the normal resource ids are not overlapping the id range for sequences
Assertf(!CScriptResource_SequenceTask::IsValidResourceId(m_NextFreeResourceId), "Script %s has created too many resources", GetLogName());
// allow the ids to wrap if there are no other sequence resources
if (m_nextFreeSequenceId >= CScriptResource_SequenceTask::MAX_NUM_SEQUENCES_PERMITTED)
{
if (GetNumScriptResourcesOfType(CGameScriptResource::SCRIPT_RESOURCE_SEQUENCE_TASK) == 0)
{
m_nextFreeSequenceId = 0;
}
else
{
Assertf(0, "Script %s has created too many task sequences (max allowed: %d)", GetLogName(), CScriptResource_SequenceTask::MAX_NUM_SEQUENCES_PERMITTED);
}
}
freeId = CScriptResource_SequenceTask::GetResourceIdFromSequenceId(++m_nextFreeSequenceId);
Assert(freeId != 0);
}
else
{
freeId = scriptHandler::GetNextFreeResourceId(resource);
}
return freeId;
}
void CGameScriptHandlerNetwork::SetMaxNumParticipants(int n)
{
m_MaxNumParticipants = static_cast<u8>(n);
if (GetNetworkComponent())
{
GetNetworkComponent()->SetMaxNumParticipants(n);
}
}
int CGameScriptHandlerNetwork::GetMaxNumParticipants() const
{
if (GetNetworkComponent())
{
return GetNetworkComponent()->GetMaxNumParticipants();
}
return static_cast<int>(m_MaxNumParticipants);
}
void CGameScriptHandlerNetwork::ReserveLocalPeds(int n)
{
if (n != m_NumLocalReservedPeds)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if(log)
{
NetworkLogUtils::WriteLogEvent(*log, "SCRIPT_RESERVING_LOCAL_PEDS", "%s %d", GetScriptId().GetLogName(), n);
}
if (gnetVerifyf(n+GetNumGlobalReservedPeds() >= m_NumCreatedPeds || (GetNetworkComponent() && !GetNetworkComponent()->IsHostLocal()), "Script: %s is reserving less peds (%d) than the number that are currently active (%d)", GetScriptName(), n+GetNumGlobalReservedPeds(), m_NumCreatedPeds))
{
m_NumLocalReservedPeds = static_cast<u8>(n);
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
}
}
void CGameScriptHandlerNetwork::ReserveLocalVehicles(int n)
{
if (n != m_NumLocalReservedVehicles)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if(log)
{
NetworkLogUtils::WriteLogEvent(*log, "SCRIPT_RESERVING_LOCAL_VEHICLES", "%s %d", GetScriptId().GetLogName(), n);
}
if (gnetVerifyf(n+GetNumGlobalReservedVehicles() >= m_NumCreatedVehicles || (GetNetworkComponent() && !GetNetworkComponent()->IsHostLocal()), "Script: %s is reserving less vehicles (%d) than the number that are currently active (%d)", GetScriptName(), n+GetNumGlobalReservedVehicles(), m_NumCreatedVehicles))
{
m_NumLocalReservedVehicles = static_cast<u8>(n);
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
}
}
void CGameScriptHandlerNetwork::ReserveLocalObjects(int n)
{
if (n != m_NumLocalReservedObjects)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if(log)
{
NetworkLogUtils::WriteLogEvent(*log, "SCRIPT_RESERVING_LOCAL_OBJECTS", "%s %d", GetScriptId().GetLogName(), n);
}
if (gnetVerifyf(n+GetNumGlobalReservedObjects() >= m_NumCreatedObjects || (GetNetworkComponent() && !GetNetworkComponent()->IsHostLocal()), "Script: %s is reserving less objects (%d) than the number that are currently active (%d)", GetScriptName(), n+GetNumGlobalReservedObjects(), m_NumCreatedObjects))
{
m_NumLocalReservedObjects = static_cast<u8>(n);
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
}
}
void CGameScriptHandlerNetwork::ReserveGlobalPeds(int n)
{
if (n != m_NumGlobalReservedPeds)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if(log)
{
NetworkLogUtils::WriteLogEvent(*log, "SCRIPT_RESERVING_GLOBAL_PEDS", "%s %d", GetScriptId().GetLogName(), n);
}
if (gnetVerifyf(n+GetNumLocalReservedPeds() >= m_NumCreatedPeds || (GetNetworkComponent() && !GetNetworkComponent()->IsHostLocal()), "Script: %s is reserving less peds (%d) than the number that are currently active (%d)", GetScriptName(), n+GetNumLocalReservedPeds(), m_NumCreatedPeds))
{
m_NumGlobalReservedPeds = static_cast<u8>(n);
CGameScriptHandlerMgr::CRemoteScriptInfo* pRemoteScriptInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(GetScriptId(), true);
if (pRemoteScriptInfo)
{
pRemoteScriptInfo->ReservePeds(n);
}
if (GetNetworkComponent() && GetNetworkComponent()->IsHostLocal())
{
static_cast<CGameScriptHandlerNetComponent*>(GetNetworkComponent())->BroadcastRemoteScriptInfo();
}
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
}
}
void CGameScriptHandlerNetwork::ReserveGlobalVehicles(int n)
{
if (n != m_NumGlobalReservedVehicles)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if(log)
{
NetworkLogUtils::WriteLogEvent(*log, "SCRIPT_RESERVING_GLOBAL_VEHICLES", "%s %d", GetScriptId().GetLogName(), n);
}
if (gnetVerifyf(n+GetNumLocalReservedVehicles() >= m_NumCreatedVehicles || (GetNetworkComponent() && !GetNetworkComponent()->IsHostLocal()), "Script: %s is reserving less vehicles (%d) than the number that are currently active (%d)", GetScriptName(), n+GetNumLocalReservedVehicles(), m_NumCreatedVehicles))
{
m_NumGlobalReservedVehicles = static_cast<u8>(n);
CGameScriptHandlerMgr::CRemoteScriptInfo* pRemoteScriptInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(GetScriptId(), true);
if (pRemoteScriptInfo)
{
pRemoteScriptInfo->ReserveVehicles(n);
}
if (GetNetworkComponent() && GetNetworkComponent()->IsHostLocal())
{
static_cast<CGameScriptHandlerNetComponent*>(GetNetworkComponent())->BroadcastRemoteScriptInfo();
}
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
}
}
void CGameScriptHandlerNetwork::ReserveGlobalObjects(int n)
{
if (n != m_NumGlobalReservedObjects)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
if(log)
{
NetworkLogUtils::WriteLogEvent(*log, "SCRIPT_RESERVING_GLOBAL_OBJECTS", "%s %d", GetScriptId().GetLogName(), n);
}
if (gnetVerifyf(n+GetNumLocalReservedObjects() >= m_NumCreatedObjects || (GetNetworkComponent() && !GetNetworkComponent()->IsHostLocal()), "Script: %s is reserving less objects (%d) than the number that are currently active (%d)", GetScriptName(), n+GetNumLocalReservedObjects(), m_NumCreatedObjects))
{
m_NumGlobalReservedObjects = static_cast<u8>(n);
CGameScriptHandlerMgr::CRemoteScriptInfo* pRemoteScriptInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(GetScriptId(), true);
if (pRemoteScriptInfo)
{
pRemoteScriptInfo->ReserveObjects(n);
}
if (GetNetworkComponent() && GetNetworkComponent()->IsHostLocal())
{
static_cast<CGameScriptHandlerNetComponent*>(GetNetworkComponent())->BroadcastRemoteScriptInfo();
}
if (log)
{
CTheScripts::GetScriptHandlerMgr().LogCurrentReservations(*log);
}
}
}
}
unsigned CGameScriptHandlerNetwork::GetNumRequiredEntities() const
{
u32 numPeds = 0;
u32 numVehicles = 0;
u32 numObjects = 0;
GetNumRequiredEntities(numPeds, numVehicles, numObjects);
return numPeds + numVehicles + numObjects;
}
void CGameScriptHandlerNetwork::GetNumRequiredEntities(u32& numRequiredPeds, u32 &numRequiredVehicles, u32 &numRequiredObjects) const
{
numRequiredPeds = numRequiredVehicles = numRequiredObjects = 0;
if (gnetVerifyf(GetTotalNumReservedPeds() >= m_NumCreatedPeds, "%s has more created peds(%d) than reserved ones(%d)", GetScriptId().GetLogName(), m_NumCreatedPeds, GetTotalNumReservedPeds()))
{
numRequiredPeds = static_cast<u32>(GetTotalNumReservedPeds() - m_NumCreatedPeds);
}
if (gnetVerifyf(GetTotalNumReservedVehicles() >= m_NumCreatedVehicles, "%s has more created vehicles(%d) than reserved ones(%d)", GetScriptId().GetLogName(), m_NumCreatedVehicles, GetTotalNumReservedVehicles()))
{
numRequiredVehicles = static_cast<u32>(GetTotalNumReservedVehicles() - m_NumCreatedVehicles);
}
if (gnetVerifyf(GetTotalNumReservedObjects() >= m_NumCreatedObjects, "%s has more created objects(%d) than reserved ones(%d)", GetScriptId().GetLogName(), m_NumCreatedObjects, GetTotalNumReservedObjects()))
{
numRequiredObjects = static_cast<u32>(GetTotalNumReservedObjects() - m_NumCreatedObjects);
}
}
void CGameScriptHandlerNetwork::AcceptPlayers(bool bAccept)
{
m_AcceptingPlayers = bAccept;
if (GetNetworkComponent() && GetNetworkComponent()->GetState() == scriptHandlerNetComponent::NETSCRIPT_PLAYING)
{
if (scriptVerify(GetNetworkComponent()->IsHostLocal()))
{
GetNetworkComponent()->AcceptPlayers(bAccept);
}
}
}
void CGameScriptHandlerNetwork::NetworkGameStarted()
{
const GtaThread* pGtaThread = static_cast<const GtaThread*>(GetThread());
// the thread may not exist at this point, if it has been terminated during the joining process
if (pGtaThread && scriptVerify(GetNetworkComponent()))
{
if (GetNetworkComponent()->IsHostLocal())
{
AcceptPlayers(m_AcceptingPlayers);
}
}
}
bool CGameScriptHandlerNetwork::CanAcceptMigratingObject(scriptHandlerObject &object, const netPlayer& receivingPlayer, eMigrationType migrationType) const
{
if (gnetVerify(m_NetComponent) && gnetVerify(object.GetScriptHandler() == this))
{
CNetObjProximityMigrateable* pNetObj = static_cast<CNetObjProximityMigrateable*>(object.GetNetObject());
if (gnetVerify(pNetObj))
{
if (pNetObj->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_SCRIPT_MIGRATION) || pNetObj->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_CLONEONLY_SCRIPT))
{
if (receivingPlayer.IsLocal())
{
if (m_NetComponent->GetState() != scriptHandlerNetComponent::NETSCRIPT_PLAYING)
return false;
if (!m_NetComponent->IsPlayerAParticipant(receivingPlayer))
return false;
}
else if (m_NetComponent->GetState() == scriptHandlerNetComponent::NETSCRIPT_TERMINATED)
{
// have to use the remote script info as we will not be informed of leaving players now as we have left the session
CGameScriptHandlerMgr::CRemoteScriptInfo* pRemoteScriptInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(GetScriptId(), true);
if (!pRemoteScriptInfo || !pRemoteScriptInfo->IsPlayerAParticipant(receivingPlayer.GetPhysicalPlayerIndex()))
return false;
}
else
{
if (!m_NetComponent->IsPlayerAParticipant(receivingPlayer))
return false;
}
}
if (migrationType == MIGRATE_PROXIMITY && pNetObj->GetPlayerOwner() && pNetObj->GetPlayerOwner()->IsLocal() && pNetObj->GetPlayerOwner()->CanAcceptMigratingObjects())
{
bool bInSameInteriorLocation = pNetObj->IsInSameInterior(*pNetObj->GetPlayerOwner()->GetPlayerPed()->GetNetworkObject());
// don't allow migration to non-participants if we are nearby the object. If we are host prevent migration if we are nearby.
if (bInSameInteriorLocation && (!m_NetComponent->IsPlayerAParticipant(receivingPlayer) || (m_NetComponent->IsHostLocal() && pNetObj->FavourMigrationToHost())))
{
float scopeDist = pNetObj->GetScopeData().m_scopeDistance;
float scopeDistSqr = scopeDist*scopeDist;
Vector3 diff = NetworkUtils::GetNetworkPlayerPosition(*pNetObj->GetPlayerOwner()) - pNetObj->GetPosition();
float distToOwner = diff.XYMag2();
if (distToOwner < scopeDistSqr)
{
return false;
}
}
}
}
}
return true;
}
#define MAX_UNREGISTERING_OBJECTS 100
// cleans up any client created script objects associated with a participant slot
void CGameScriptHandlerNetwork::CleanupAllClientObjects(ScriptSlotNumber clientSlot)
{
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
scriptHandlerObject* objectsToUnregister[MAX_UNREGISTERING_OBJECTS];
u32 numObjectsToUnregister = 0;
while (node)
{
scriptHandlerObject* pObject = node->Data;
if (AssertVerify(pObject) &&
AssertVerify(pObject->GetScriptInfo()) &&
GET_SLOT_FROM_SCRIPT_OBJECT_ID(pObject->GetScriptInfo()->GetObjectId()) == (u32)clientSlot)
{
char logName[30];
pObject->GetScriptInfo()->GetLogName(logName, 30);
NetworkLogUtils::WriteLogEvent(*log, "UNREGISTER_PREVIOUS_CLIENT_OBJECT", "%s %s", GetScriptId().GetLogName(), logName);
if (AssertVerify(numObjectsToUnregister<MAX_UNREGISTERING_OBJECTS-1))
{
objectsToUnregister[numObjectsToUnregister++] = pObject;
}
}
node = node->GetNext();
}
for (u32 i=0; i<numObjectsToUnregister; i++)
{
RemoveOldScriptObject(*objectsToUnregister[i]);
}
}
bool CGameScriptHandlerNetwork::MigrateObjects()
{
bool bObjectsToMigrate = false;
if (!AssertVerify(m_NetComponent))
{
return false;
}
const CNetGamePlayer* aParticipants[MAX_NUM_PHYSICAL_PLAYERS];
u32 numParticipants = 0;
const netPlayer* pHost = NULL;
// have to use the remote script info as we will not be informed of leaving players now as we have left the session
CGameScriptHandlerMgr::CRemoteScriptInfo* pRemoteScriptInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(GetScriptId(), true);
if (pRemoteScriptInfo)
{
unsigned numRemotePhysicalPlayers = netInterface::GetNumRemotePhysicalPlayers();
const netPlayer * const *remotePhysicalPlayers = netInterface::GetRemotePhysicalPlayers();
for(unsigned index = 0; index < numRemotePhysicalPlayers; index++)
{
const CNetGamePlayer *pPlayer = SafeCast(const CNetGamePlayer, remotePhysicalPlayers[index]);
if (pRemoteScriptInfo->IsPlayerAParticipant(pPlayer->GetPhysicalPlayerIndex()))
{
aParticipants[numParticipants++] = pPlayer;
}
}
pHost = pRemoteScriptInfo->GetHost();
}
static const u32 MAX_OBJECTS_TO_REMOVE = 128;
CNetObjProximityMigrateable* objectsToRemove[MAX_OBJECTS_TO_REMOVE];
u32 numObjectsToRemove = 0;
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
scriptHandlerObject* pObject = node->Data;
if (AssertVerify(pObject))
{
CNetObjProximityMigrateable* pNetObj = static_cast<CNetObjProximityMigrateable*>(pObject->GetNetObject());
if (pNetObj && AssertVerify(pObject->GetScriptInfo()))
{
if (!pNetObj->IsClone() && pNetObj->IsGlobalFlagSet(netObject::GLOBALFLAG_PERSISTENTOWNER))
{
pNetObj->SetGlobalFlag(netObject::GLOBALFLAG_PERSISTENTOWNER, false);
}
if (pNetObj->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_SCRIPT_MIGRATION) || pNetObj->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_CLONEONLY_SCRIPT))
{
if (pNetObj->IsLocalFlagSet(netObject::LOCALFLAG_BEINGREASSIGNED))
{
// we must wait for the object to be reassigned, otherwise it may be reassigned to our machine after the script handler has terminated
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "Cannot terminate: waiting for %s to be reassigned", pNetObj->GetLogName());
bObjectsToMigrate = true;
}
else if (!pNetObj->IsClone())
{
bool bRemove = numParticipants == 0;
if (pNetObj->IsPendingOwnerChange())
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "Cannot terminate: waiting for %s to migrate to %s", pNetObj->GetLogName(), pNetObj->GetPendingPlayerOwner() ? pNetObj->GetPendingPlayerOwner()->GetLogName() : "??");
bObjectsToMigrate = true;
bRemove = false;
}
else if (!bRemove)
{
bObjectsToMigrate = true;
const CNetGamePlayer* pParticipantToMigrateTo = 0;
Vector3 diff;
float closestDist = -1.0f;
float scopeDist = pNetObj->GetScopeDistance(pParticipantToMigrateTo);
float scopeDistSqr = scopeDist*scopeDist;
for (u32 i=0; i<numParticipants; i++)
{
const CNetGamePlayer* pParticipant = aParticipants[i];
// check that the player is still a participant (he may have left after we informed him that we left)
if (CTheScripts::GetScriptHandlerMgr().IsPlayerAParticipant(GetScriptId(), *pParticipant))
{
if (pNetObj->HasBeenCloned(*pParticipant))
{
diff = NetworkUtils::GetNetworkPlayerPosition(*pParticipant) - pNetObj->GetPosition();
const float dist = diff.XYMag2();
if (!pParticipantToMigrateTo || dist < closestDist)
{
pParticipantToMigrateTo = pParticipant;
closestDist = dist;
}
else if (dist < scopeDistSqr)
{
// favour the host if he is nearby
if (static_cast<const CNetGamePlayer*>(pHost) == pParticipant)
{
pParticipantToMigrateTo = pParticipant;
break;
}
}
}
}
}
if (pParticipantToMigrateTo)
{
unsigned resultCode = 0;
if (pNetObj->CanPassControl(*pParticipantToMigrateTo, MIGRATE_FORCED, &resultCode))
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "Cannot terminate: starting to migrate %s to %s", pNetObj->GetLogName(), pParticipantToMigrateTo->GetLogName());
CGiveControlEvent::Trigger(*pParticipantToMigrateTo, pNetObj, MIGRATE_FORCED);
}
else
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "Cannot terminate: can't pass control of %s to %s (Reason: %s)", pNetObj->GetLogName(), pParticipantToMigrateTo->GetLogName(), NetworkUtils::GetCanPassControlErrorString(resultCode));
}
}
else
{
bRemove = true;
}
}
if (bRemove && AssertVerify(numObjectsToRemove < MAX_OBJECTS_TO_REMOVE))
{
// no participants left to take the object, so remove it
objectsToRemove[numObjectsToRemove++] = pNetObj;
}
}
}
}
}
node = node->GetNext();
}
for (u32 i=0; i<numObjectsToRemove; i++)
{
objectsToRemove[i]->PrepareForScriptTermination();
netInterface::GetObjectManager().UnregisterNetworkObject(objectsToRemove[i], CNetworkObjectMgr::SCRIPT_CLONE_ONLY, false, true);
}
if (!bObjectsToMigrate)
{
if (numParticipants == 0)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "No objects to migrate, no participants left");
}
else
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "No objects to migrate");
}
}
return bObjectsToMigrate;
}
#if __DEV
void CGameScriptHandlerNetwork::DisplayScriptHandlerInfo() const
{
CGameScriptHandler::DisplayScriptHandlerInfo();
if (m_NetComponent)
{
static_cast<CGameScriptHandlerNetComponent*>(m_NetComponent)->DisplayScriptHandlerInfo();
grcDebugDraw::AddDebugOutput("Reserved : peds=%d/%d, vehicles=%d/%d, objects=%d/%d",
m_NumCreatedPeds, GetTotalNumReservedPeds(),
m_NumCreatedVehicles, GetTotalNumReservedVehicles(),
m_NumCreatedObjects, GetTotalNumReservedObjects());
if (m_NetComponent)
{
grcDebugDraw::AddDebugOutput("Bandwidth: Total in = %d, Total out = %d, Avg in = %d, Avg out = %d, Peak in = %d, Peak out = %d", m_NetComponent->GetTotalBandwidthIn(), m_NetComponent->GetTotalBandwidthOut(), m_NetComponent->GetAverageBandwidthIn(), m_NetComponent->GetAverageBandwidthOut(), m_NetComponent->GetPeakBandwidthIn(), m_NetComponent->GetPeakBandwidthOut());
for (int i=0; i<m_NetComponent->GetNumHostBroadcastDatasRegistered(); i++)
{
netScriptBroadcastDataHandlerBase *hostHandler = m_NetComponent->GetHostBroadcastDataHandler(i);
if (gnetVerify(hostHandler))
{
grcDebugDraw::AddDebugOutput("Host broadcast data handler %d : total bandwidth out = %d, average out = %d, peak out = %d", i, hostHandler->GetTotalBandwidthOut(), hostHandler->GetAverageBandwidthOut(m_NetComponent->GetTimeHandlerActive()), hostHandler->GetPeakBandwidthOut());
}
}
for (int i=0; i<m_NetComponent->GetNumPlayerBroadcastDatasRegistered(); i++)
{
netScriptBroadcastDataHandlerBase *playerHandler = m_NetComponent->GetLocalPlayerBroadcastDataHandler(i);
if (gnetVerify(playerHandler))
{
grcDebugDraw::AddDebugOutput("Player broadcast data handler %d : total bandwidth out = %d, average out = %d, peak out = %d", i, playerHandler->GetTotalBandwidthOut(), playerHandler->GetAverageBandwidthOut(m_NetComponent->GetTimeHandlerActive()), playerHandler->GetPeakBandwidthOut());
}
}
}
}
}
void CGameScriptHandlerNetwork::MarkCanRegisterMissionEntitiesCalled(u32 numPeds, u32 numVehicles, u32 numObjects)
{
gnetAssert(numPeds<(1<<(sizeof(m_canRegisterMissionPedCalled)*8)));
gnetAssert(numVehicles<(1<<(sizeof(m_canRegisterMissionVehicleCalled)*8)));
gnetAssert(numObjects<(1<<(sizeof(m_canRegisterMissionObjectCalled)*8)));
if (numPeds > 0)
{
m_canRegisterMissionPedCalled = static_cast<u8>(numPeds);
}
if (numVehicles > 0)
{
m_canRegisterMissionVehicleCalled = static_cast<u8>(numVehicles);
}
if (numObjects > 0)
{
m_canRegisterMissionObjectCalled = static_cast<u8>(numObjects);
}
CNetworkObjectPopulationMgr::eVehicleGenerationContext eOldGenContext = CNetworkObjectPopulationMgr::GetVehicleGenerationContext();
CNetworkObjectPopulationMgr::SetVehicleGenerationContext(CNetworkObjectPopulationMgr::VGT_Script);
gnetAssert(NetworkInterface::CanRegisterObjects(m_canRegisterMissionPedCalled, m_canRegisterMissionVehicleCalled, m_canRegisterMissionObjectCalled, 0, 0, true));
CNetworkObjectPopulationMgr::SetVehicleGenerationContext(eOldGenContext);
}
#endif // __DEV
#if __BANK
void CGameScriptHandlerNetwork::DebugPrintUnClonedEntities()
{
if (m_NetComponent && GetThread() && NetworkInterface::GetLocalPlayer() && NetworkInterface::GetLocalPlayer()->GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX)
{
PlayerFlags playerParticipants = m_NetComponent->GetPlayerParticipants();
// don't include our local player (a network object's cloned state won't)
playerParticipants &= ~(1 << NetworkInterface::GetLocalPlayer()->GetPhysicalPlayerIndex());
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
scriptHandlerObject* pScriptObj = node->Data;
if (pScriptObj && AssertVerify(pScriptObj->GetScriptInfo()) && pScriptObj->GetScriptInfo()->IsScriptHostObject())
{
CNetObjGame* pNetObj = static_cast<CNetObjGame*>(pScriptObj->GetNetObject());
if (pNetObj &&
!pNetObj->IsClone() &&
!pNetObj->IsPendingOwnerChange() &&
pNetObj->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_CLONEALWAYS_SCRIPT) &&
pNetObj->RestrictsBroadcastDataWhenUncloned())
{
PlayerFlags clonedState = pNetObj->GetClonedState();
if ((clonedState & playerParticipants) != playerParticipants)
{
for (u32 i = 0; i < MAX_NUM_PHYSICAL_PLAYERS; i++)
{
if ((playerParticipants & (1 << i)) && !(clonedState & (1 << i)))
{
CNetGamePlayer* pPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(static_cast<PhysicalPlayerIndex>(i));
gnetDebug1("%s waiting to be clone on %s", pNetObj->GetLogName(), pPlayer ? pPlayer->GetLogName() : "(Missing Player)");
}
}
}
}
}
node = node->GetNext();
}
}
}
#endif // __BANK
bool CGameScriptHandlerNetwork::HaveAllScriptEntitiesFinishedCloning() const
{
bool bFinishedCloning = true;
if (m_NetComponent && GetThread() && NetworkInterface::GetLocalPlayer() && NetworkInterface::GetLocalPlayer()->GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX)
{
PlayerFlags playerParticipants = m_NetComponent->GetPlayerParticipants();
// don't include our local player (a network object's cloned state won't)
playerParticipants &= ~(1<<NetworkInterface::GetLocalPlayer()->GetPhysicalPlayerIndex());
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
scriptHandlerObject* pScriptObj = node->Data;
if (pScriptObj && AssertVerify(pScriptObj->GetScriptInfo()) && pScriptObj->GetScriptInfo()->IsScriptHostObject())
{
CNetObjGame* pNetObj = static_cast<CNetObjGame*>(pScriptObj->GetNetObject());
if (pNetObj &&
!pNetObj->IsClone() &&
!pNetObj->IsPendingOwnerChange() &&
pNetObj->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_CLONEALWAYS_SCRIPT) &&
pNetObj->RestrictsBroadcastDataWhenUncloned())
{
PlayerFlags clonedState = pNetObj->GetClonedState();
if ((clonedState & playerParticipants) != playerParticipants)
{
for (u32 i=0; i<MAX_NUM_PHYSICAL_PLAYERS; i++)
{
if ((playerParticipants & (1<<i)) && !(clonedState & (1<<i)))
{
CNetGamePlayer* pPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(static_cast<PhysicalPlayerIndex>(i));
if (pPlayer)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "*** Broadcast data update delayed - waiting on %s to clone on %s ***", pNetObj->GetLogName(), pPlayer->GetLogName());
}
else
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "**** Broadcast data update delayed - waiting on %s to clone on non-existent player %d ***", pNetObj->GetLogName(), i);
}
}
}
bFinishedCloning = false;
}
}
}
node = node->GetNext();
}
}
return bFinishedCloning;
}
void CGameScriptHandlerNetwork::RegisterAllExistingScriptObjects()
{
// When a script handler is created, any objects which are associated with the script need to be registered with it. The objects
// may exist because they were created by another machine running the script, or because a script is starting up again and a
// previous instance of it left objects behind. The registering is done here and not in the object manager to avoid a dependency
// between the object manager and the script code.
netObject* objects[MAX_NUM_NETOBJECTS];
for (int i=0; i<MAX_NUM_NETOBJECTS; i++)
{
objects[i] = 0;
}
unsigned numObjects = netInterface::GetObjectManager().GetAllObjects(objects, MAX_NUM_NETOBJECTS, true, true);
for(unsigned index = 0; index < numObjects; index++)
{
if (gnetVerify(objects[index]) &&
gnetVerify(objects[index]->GetScriptHandlerObject()) &&
!objects[index]->GetScriptHandlerObject()->GetScriptHandler())
{
const scriptObjInfoBase *scriptInfo = objects[index]->GetScriptObjInfo();
if (scriptInfo && scriptInfo->GetScriptId() == GetScriptId())
{
RegisterExistingScriptObjectInternal(*objects[index]->GetScriptHandlerObject());
}
}
}
}
void CGameScriptHandlerNetwork::RegisterExistingScriptObjectInternal(scriptHandlerObject &object)
{
// the script id must have a timestamp to distinguish it from other scripts that have ran in the past
gnetAssert(static_cast<const CGameScriptId&>(GetScriptId()).GetTimeStamp() != 0 || object.IsUnnetworkedObject());
// if this a client object created by a previous participant that has left, clean it up
netObject* pNetObj = object.GetNetObject();
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
char logName[30];
if (pNetObj)
{
if (object.IsHostObject())
{
if (GetNetworkComponent()->IsHostLocal())
{
ScriptObjectId objectIdIndex = GET_INDEX_FROM_SCRIPT_OBJECT_ID(object.GetScriptInfo()->GetObjectId());
// unregister the object if we are the host and it is an object created by a previous host with a higher host object id
if (objectIdIndex > m_NextFreeHostObjectId)
{
if (log)
{
object.GetScriptInfo()->GetLogName(logName, 30);
NetworkLogUtils::WriteLogEvent(*log, "REGISTER_EXISTING_HOST_ENTITY", "%s %s", object.GetScriptInfo()->GetScriptId().GetLogName(), logName);
log->WriteDataValue("Script id", "%d", object.GetScriptInfo()->GetObjectId());
log->WriteDataValue("Net Object", "%s", pNetObj ? pNetObj->GetLogName() : "-none-");
}
if (pNetObj->IsLocalFlagSet(CNetObjGame::LOCALFLAG_NOLONGERNEEDED))
{
if(log)
{
log->WriteDataValue("*Failed*", "Entity already flagged to be cleaned up");
}
}
else
{
if(log)
{
log->WriteDataValue("*Failed*", "Entity to be cleaned up");
log->WriteDataValue("Reason", "Entity created by previous host with a higher object id than our current one (%d, current: %d)", objectIdIndex, m_NextFreeHostObjectId);
log->WriteDataValue("Action", "Inform owner (%s) to delete this entity", pNetObj->GetPlayerOwner() ? pNetObj->GetPlayerOwner()->GetLogName() : "<none>");
pNetObj->SetLocalFlag(CNetObjGame::LOCALFLAG_NOLONGERNEEDED, true);
// tell the owner to delete the entity - we can have the situation where the owner is not running the script anymore, and we are the only machine
// that knows about the duplicate
CMarkAsNoLongerNeededEvent::Trigger(*static_cast<CNetObjGame*>(pNetObj), true);
}
}
return;
}
}
if (pNetObj->GetEntity())
{
ValidateRegistrationRequest(static_cast<CPhysical*>(pNetObj->GetEntity()), false);
}
}
else
{
int participantSlot = GET_SLOT_FROM_SCRIPT_OBJECT_ID(object.GetScriptInfo()->GetObjectId());
bool bPreviousClientEntity = (participantSlot == GetNetworkComponent()->GetLocalSlot()) && !GetNetworkComponent()->IsPlaying();
bool bParticipantNotActive = !GetNetworkComponent()->IsParticipantActive(participantSlot);
if (bPreviousClientEntity || bParticipantNotActive)
{
if (log)
{
object.GetScriptInfo()->GetLogName(logName, 30);
NetworkLogUtils::WriteLogEvent(*log, "REGISTER_EXISTING_LOCAL_ENTITY", "%s %s", object.GetScriptInfo()->GetScriptId().GetLogName(), logName);
log->WriteDataValue("Script id", "%d", object.GetScriptInfo()->GetObjectId());
log->WriteDataValue("Net Object", "%s", pNetObj ? pNetObj->GetLogName() : "-none-");
}
if (pNetObj->IsLocalFlagSet(CNetObjGame::LOCALFLAG_NOLONGERNEEDED))
{
if(log)
{
log->WriteDataValue("*Failed*", "Entity already flagged to be cleaned up");
}
}
else
{
if(log)
{
log->WriteDataValue("*Failed*", "Entity to be cleaned up");
if (bPreviousClientEntity)
{
log->WriteDataValue("Reason", "Entity created by previous client in this slot (%d)", participantSlot);
}
else if (bParticipantNotActive)
{
log->WriteDataValue("Reason", "Participant in slot %d no longer active", participantSlot);
}
}
RemoveOldScriptObject(object);
}
return;
}
}
}
CGameScriptHandler::RegisterExistingScriptObjectInternal(object);
if (!object.GetScriptHandler())
{
gnetAssertf(0, "Script object failed to register");
}
}
void CGameScriptHandlerNetwork::RemoveOldScriptObject(scriptHandlerObject &object)
{
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
netObject* pNetObj = object.GetNetObject();
char logName[30];
object.GetScriptInfo()->GetLogName(logName, 30);
if(log)
{
if (object.IsHostObject())
{
NetworkLogUtils::WriteLogEvent(*log, "REMOVE_OLD_HOST_OBJECT", "%s %s", GetScriptId().GetLogName(), logName);
}
else
{
NetworkLogUtils::WriteLogEvent(*log, "REMOVE_OLD_CLIENT_OBJECT", "%s %s", GetScriptId().GetLogName(), logName);
}
log->WriteDataValue("Script id", "%d", object.GetScriptInfo()->GetObjectId());
log->WriteDataValue("Net Object", "%s", pNetObj ? pNetObj->GetLogName() : "-none-");
}
if (pNetObj)
{
bool bIsClone = pNetObj->IsClone() || pNetObj->IsPendingOwnerChange();
// try to remove host entities, previous client entities can stick around
bool bToBeDeleted = object.IsHostObject() && object.GetEntity();
pNetObj->SetLocalFlag(CNetObjGame::LOCALFLAG_NOLONGERNEEDED, true);
if (bIsClone && GetNetworkComponent())
{
bool bTriggerEvent = GetNetworkComponent()->IsHostLocal();
if (!bTriggerEvent && !object.IsHostObject())
{
int participantSlot = GET_SLOT_FROM_SCRIPT_OBJECT_ID(object.GetScriptInfo()->GetObjectId());
bTriggerEvent = (participantSlot == GetNetworkComponent()->GetLocalSlot());
}
if (bTriggerEvent)
{
CMarkAsNoLongerNeededEvent::Trigger(*static_cast<CNetObjGame*>(pNetObj), bToBeDeleted);
}
}
if (bToBeDeleted)
{
CNetObjEntity* pEntityObj = SafeCast(CNetObjEntity, pNetObj);
if (!bIsClone && pEntityObj->CanDelete())
{
if (log)
{
NetworkLogUtils::WriteLogEvent(*log, "UNREGISTERING_ENTITY", "%s %s", GetScriptId().GetLogName(), logName);
log->WriteDataValue("Net Object", "%s", pNetObj ? pNetObj->GetLogName() : "-none-");
}
netInterface::GetObjectManager().UnregisterNetworkObject(pEntityObj, CNetworkObjectMgr::DUPLICATE_SCRIPT_OBJECT, false, true);
return;
}
else
{
if (log)
{
NetworkLogUtils::WriteLogEvent(*log, "FLAGGING_ENTITY_FOR_DELETION", "%s %s", GetScriptId().GetLogName(), logName);
log->WriteDataValue("Net Object", "%s", pNetObj ? pNetObj->GetLogName() : "-none-");
}
pEntityObj->FlagForDeletion();
}
}
}
// the object may not have been registered with this script yet
if (object.GetScriptHandler())
{
scriptHandler::RemoveOldScriptObject(object);
}
}
void CGameScriptHandlerNetwork::RemoveOldHostObjects()
{
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
scriptHandlerObject* objectsToUnregister[MAX_UNREGISTERING_OBJECTS];
u32 numObjectsToUnregister = 0;
while (node)
{
scriptHandlerObject* pScriptObj = node->Data;
const scriptObjInfoBase *pScriptInfo = pScriptObj ? pScriptObj->GetScriptInfo() : NULL;
if (pScriptInfo && pScriptObj->IsHostObject() && GET_INDEX_FROM_SCRIPT_OBJECT_ID(pScriptInfo->GetObjectId()) > m_NextFreeHostObjectId)
{
if (AssertVerify(numObjectsToUnregister<MAX_UNREGISTERING_OBJECTS-1))
{
objectsToUnregister[numObjectsToUnregister++] = pScriptObj;
}
}
node = node->GetNext();
}
for (u32 i=0; i<numObjectsToUnregister; i++)
{
scriptHandlerObject* pScriptObj = objectsToUnregister[i];
RemoveOldScriptObject(*pScriptObj);
}
}
void CGameScriptHandlerNetwork::ValidateRegistrationRequest(CPhysical* pTargetEntity, bool DEV_ONLY(bNewObject))
{
#if __ASSERT
bool bIAmHost = gnetVerify(m_NetComponent) && m_NetComponent->IsHostLocal();
#endif
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
#if __ASSERT
const char* modelName = pTargetEntity && pTargetEntity->GetModelName() ? pTargetEntity->GetModelName() : "-none-";
#endif
bool bTargetEntityIsLocalReservation = pTargetEntity && pTargetEntity->GetNetworkObject() && pTargetEntity->GetNetworkObject()->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_CLONEONLY_SCRIPT);
if (pTargetEntity->GetIsTypePed())
{
m_NumCreatedPeds++;
if (m_NumCreatedPeds > GetTotalNumReservedPeds())
{
ASSERT_ONLY(gnetAssertf(!bIAmHost || !bNewObject, "Script: %s is creating more peds than it has reserved (ped model : %s)", GetScriptName(), modelName);)
if (bTargetEntityIsLocalReservation)
{
m_NumLocalReservedPeds = m_NumCreatedPeds-m_NumGlobalReservedPeds;
}
else
{
m_NumGlobalReservedPeds = m_NumCreatedPeds-m_NumLocalReservedPeds;
}
}
#if __ASSERT
if (bIAmHost && bNewObject)
{
gnetAssertf(m_canRegisterMissionPedCalled > 0, "Script: %s is creating more peds than have been verified using CAN_REGISTER_MISSION_PEDS (ped model: %s)", GetScriptName(), modelName);
if(m_canRegisterMissionPedCalled > 0)
{
m_canRegisterMissionPedCalled--;
}
}
#endif // __DEV
if(log)
{
log->WriteDataValue("Ped Reservation", "%d/%d (%d)", m_NumCreatedPeds, GetTotalNumReservedPeds(), m_NumLocalReservedPeds);
}
}
else if (pTargetEntity->GetIsTypeVehicle())
{
m_NumCreatedVehicles++;
if (m_NumCreatedVehicles > GetTotalNumReservedVehicles())
{
ASSERT_ONLY(gnetAssertf(!bIAmHost || !bNewObject, "Script: %s is creating more vehicles than it has reserved (vehicle model : %s)", GetScriptName(), modelName);)
if (bTargetEntityIsLocalReservation)
{
m_NumLocalReservedVehicles = m_NumCreatedVehicles-m_NumGlobalReservedVehicles;
}
else
{
m_NumGlobalReservedVehicles = m_NumCreatedVehicles-m_NumLocalReservedVehicles;
}
}
#if __ASSERT
if (bIAmHost)
{
if (bNewObject)
{
gnetAssertf(m_canRegisterMissionVehicleCalled > 0, "Script: %s is creating more vehicles than have been verified using CAN_REGISTER_MISSION_VEHICLES (vehicle model: %s)", GetScriptName(), modelName);
if(m_canRegisterMissionVehicleCalled > 0)
{
m_canRegisterMissionVehicleCalled--;
}
}
}
#endif // __DEV
if(log)
{
log->WriteDataValue("Vehicle Reservation", "%d/%d (%d)", m_NumCreatedVehicles, GetTotalNumReservedVehicles(), m_NumLocalReservedVehicles);
}
}
else if (pTargetEntity->GetIsTypeObject())
{
// ignore doors & pickups
if (!static_cast<CObject*>(pTargetEntity)->IsADoor() && !static_cast<CObject*>(pTargetEntity)->m_nObjectFlags.bIsPickUp)
{
m_NumCreatedObjects++;
if (GetTotalNumReservedObjects() < m_NumCreatedObjects)
{
ASSERT_ONLY(gnetAssertf(!bNewObject, "Script: %s is creating more objects than it has reserved (object model : %s)", GetScriptName(), modelName);)
if (bTargetEntityIsLocalReservation)
{
m_NumLocalReservedObjects = m_NumCreatedObjects-m_NumGlobalReservedObjects;
}
else
{
m_NumGlobalReservedObjects = m_NumCreatedObjects-m_NumLocalReservedObjects;
}
}
#if __ASSERT
if (bIAmHost && bNewObject)
{
gnetAssertf(m_canRegisterMissionObjectCalled > 0, "Script: %s is creating more objects than have been verified using CAN_REGISTER_MISSION_OBJECTS (object model: %s)", GetScriptName(), modelName);
if(m_canRegisterMissionObjectCalled > 0)
{
m_canRegisterMissionObjectCalled--;
}
}
#endif // __DEV
if(log)
{
log->WriteDataValue("Object Reservation", "%d/%d (%d)", m_NumCreatedObjects, GetTotalNumReservedObjects(), m_NumLocalReservedObjects);
}
}
}
else
{
gnetAssertf(0, "Unexpected target object type encountered!");
}
}
// ================================================================================================================
// CGameScriptHandlerNetComponent
// ================================================================================================================
CGameScriptHandlerNetComponent::CGameScriptHandlerNetComponent(CGameScriptHandler& parentHandler, u32 maxNumParticipants)
: scriptHandlerNetComponent(parentHandler)
, m_JoinEventFlags(0)
, m_PendingTimestamp(0)
, m_bScriptReady(false)
, m_bStartedPlaying(false)
, m_bDoneLeaveCleanup(false)
, m_bDoneTerminationCleanup(false)
, m_bBroadcastScriptInfo(false)
#if __BANK
, m_joiningTimer(0)
, m_noHostTimer(0)
#endif
{
SetMaxNumParticipants(maxNumParticipants);
if (static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()).GetTimeStamp() != 0)
{
Assertf(0, "Script %s is being networked after having been previously networked. This probably means that it was kept running between two separate network sessions", m_ParentHandler.GetScriptId().GetLogName());
static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()).ResetTimestamp();
}
}
void CGameScriptHandlerNetComponent::TriggerJoinEvents()
{
// wait until a new participant is fully into the game (has joined our roaming bubble and cloned his player ped) before informing the script
if (m_JoinEventFlags != 0)
{
if (m_State == NETSCRIPT_INTERNAL_START)
{
m_JoinEventFlags = 0;
}
else if (m_State == NETSCRIPT_INTERNAL_PLAYING && !m_Terminated)
{
for (unsigned i=0; i<GetMaxNumParticipants(); i++)
{
if (m_JoinEventFlags & (1<<i))
{
const CNetGamePlayer* pParticipant = static_cast<const CNetGamePlayer*>(GetParticipantUsingSlot(i));
if (gnetVerify(pParticipant) && pParticipant->IsPhysical() && pParticipant->GetPlayerPed())
{
if (m_ParentHandler.GetThread())
{
// special leave type
CEventNetworkPlayerJoinScript::eSource nSource = CEventNetworkPlayerJoinScript::SOURCE_NORMAL;
if(pParticipant->HasStartedTransition())
nSource = CEventNetworkPlayerJoinScript::SOURCE_TRANSITION;
else if(CNetwork::GetNetworkSession().DidGamerLeaveForStore(pParticipant->GetGamerInfo().GetGamerHandle()))
nSource = CEventNetworkPlayerJoinScript::SOURCE_STORE;
if (!GetEventScriptNetworkGroup()->CollateJoinScriptEventForPlayer(*static_cast<const netPlayer*>(pParticipant), nSource, m_ParentHandler.GetThread()->GetThreadId()))
{
CEventNetworkPlayerJoinScript joinEvent(m_ParentHandler.GetThread()->GetThreadId(), *pParticipant, nSource);
GetEventScriptNetworkGroup()->Add(joinEvent);
}
}
m_JoinEventFlags &= ~(1<<i);
}
}
}
}
}
}
bool CGameScriptHandlerNetComponent::Update()
{
#if __BANK
#define DEBUG_WARNING_TIME 120000 // 2 minutes
if (m_State > NETSCRIPT_INTERNAL_START && m_State < NETSCRIPT_INTERNAL_PLAYING)
{
// display a message on screen when the script has been taking more than the timeout time to join
// this is to avoid bugs being reported when the script fails to start up
m_joiningTimer += fwTimer::GetTimeStepInMilliseconds();
if (m_joiningTimer > DEBUG_WARNING_TIME)
{
#if ENABLE_NETWORK_LOGGING
if (CTheScripts::GetScriptHandlerMgr().GetLog() &&
CTheScripts::GetScriptHandlerMgr().GetLog()->IsEnabled())
{
char message[100];
formatf(message, "%s cannot start - a machine is not responding", m_ParentHandler.GetScriptId().GetLogName());
NetworkDebug::DisplayDebugMessage(message);
}
#endif // ENABLE_NETWORK_LOGGING
}
// start logging after 5 secs
if (m_joiningTimer > 5000)
{
for (u32 i=0; i<MAX_NUM_PHYSICAL_PLAYERS; i++)
{
if (m_WaitingForAcks & (1<<i))
{
CNetGamePlayer* pPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(static_cast<PhysicalPlayerIndex>(i));
if (pPlayer)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "*** Delayed response from %s *** ", pPlayer->GetLogName());
}
else
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "*** Delayed response from non-existent player in slot %d ***", i);
}
}
}
}
}
else
{
m_joiningTimer = 0;
}
if (m_State == NETSCRIPT_INTERNAL_PLAYING && !GetHost())
{
m_noHostTimer += fwTimer::GetTimeStepInMilliseconds();
if (m_noHostTimer > DEBUG_WARNING_TIME)
{
#if ENABLE_NETWORK_LOGGING
if (CTheScripts::GetScriptHandlerMgr().GetLog() &&
CTheScripts::GetScriptHandlerMgr().GetLog()->IsEnabled())
{
char message[200];
formatf(message, "%s has no host. Report this as an A.", m_ParentHandler.GetScriptId().GetLogName());
NetworkDebug::DisplayDebugMessage(message);
}
#endif // ENABLE_NETWORK_LOGGING
}
// start logging after 5 secs
if (m_noHostTimer > 5000)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "*** No host (Pending : %s) *** ", m_PendingHost ? m_PendingHost->GetLogName() : "-none-");
if (m_noHostTimer > DEBUG_WARNING_TIME)
{
Assertf(0, "%s has no host. Please provide logs for all machines as this is serious", m_ParentHandler.GetScriptId().GetLogName());
}
}
}
else
{
m_noHostTimer = 0;
}
#endif // __BANK
bool bCanTerminate = scriptHandlerNetComponent::Update();
#if __BANK
if (bCanTerminate)
{
Displayf("The handler for script %s has terminated\n", m_ParentHandler.GetScriptId().GetLogName());
}
else if (m_State == NETSCRIPT_INTERNAL_LEAVE)
{
Displayf("The handler for script %s is terminating: waiting for broadcast data to sync\n", m_ParentHandler.GetScriptId().GetLogName());
}
else if (m_State == NETSCRIPT_INTERNAL_LEAVING)
{
Displayf("The handler for script %s is terminating: waiting for leave event responses\n", m_ParentHandler.GetScriptId().GetLogName());
}
#endif
if (m_bBroadcastScriptInfo && (!IsHostLocal() || BroadcastRemoteScriptInfo()))
{
m_bBroadcastScriptInfo = false;
}
return bCanTerminate;
}
void CGameScriptHandlerNetComponent::PlayerHasJoined(const netPlayer& player)
{
if(static_cast<const CNetGamePlayer &>(player).GetPlayerType() == CNetGamePlayer::NETWORK_PLAYER)
{
if (IsHostLocal())
{
// inform the player about this active script
if (!BroadcastRemoteScriptInfo(&player))
{
m_bBroadcastScriptInfo = true;
}
}
scriptHandlerNetComponent::PlayerHasJoined(player);
}
}
void CGameScriptHandlerNetComponent::HandleJoinAckMsg(const msgScriptJoinAck& msg, const ReceivedMessageData &messageData)
{
if (msg.m_AckCode == SCRIPT_ACK_CODE_PARTICIPANT)
{
unsigned msgTimestamp = static_cast<CGameScriptId*>(msg.m_ScriptId)->GetTimeStamp();
if (m_PendingTimestamp == 0)
{
m_PendingTimestamp = msgTimestamp;
}
else if (msgTimestamp != 0 && m_PendingTimestamp != msgTimestamp)
{
gnetAssertf(0, "Got a join ack message from a participant with an unexpected timestamp!");
ClearWaitingForAck(*messageData.m_FromPlayer);
return;
}
}
scriptHandlerNetComponent::HandleJoinAckMsg(msg, messageData);
}
void CGameScriptHandlerNetComponent::HandleJoinHostAckMsg(const msgScriptJoinHostAck& msg, const ReceivedMessageData &messageData)
{
bool bIsAParticipant = IsPlayerAParticipant(*NetworkInterface::GetLocalPlayer());
scriptHandlerNetComponent::HandleJoinHostAckMsg(msg, messageData);
if (!bIsAParticipant && IsPlayerAParticipant(*NetworkInterface::GetLocalPlayer()))
{
unsigned msgTimestamp = static_cast<CGameScriptId*>(msg.m_ScriptId)->GetTimeStamp();
gnetAssertf(msgTimestamp != 0, "Got a join host ack message from a host with a 0 timestamp!");
gnetAssertf(m_PendingTimestamp == 0 || m_PendingTimestamp == msgTimestamp, "Got a join host ack message from a host with an unexpected timestamp!");
m_PendingTimestamp = msgTimestamp;
}
}
void CGameScriptHandlerNetComponent::MigrateScriptHost(const netPlayer& player)
{
scriptHandlerNetComponent::MigrateScriptHost(player);
if (m_State == NETSCRIPT_INTERNAL_PLAYING && m_ParentHandler.GetThread())
{
CEventNetworkAttemptHostMigration migrateEvent(m_ParentHandler.GetThread()->GetThreadId(), static_cast<const CNetGamePlayer&>(player));
GetEventScriptNetworkGroup()->Add(migrateEvent);
}
}
CNetGamePlayer* CGameScriptHandlerNetComponent::GetClosestParticipant(const Vector3& pos, bool bIncludeMyPlayer) const
{
const CNetGamePlayer* pClosestPlayer = NULL;
float closestDist = 0.0f;
for (ScriptSlotNumber slot =0; slot<m_MaxNumParticipants; slot++)
{
const participantData* pParticipant = m_ParticipantsArray[slot];
if (pParticipant)
{
const CNetGamePlayer* pPlayer = static_cast<const CNetGamePlayer*>(pParticipant->GetPlayer());
if (pPlayer->GetPlayerPed() && (bIncludeMyPlayer || !pPlayer->IsMyPlayer()))
{
Vector3 playerPos = VEC3V_TO_VECTOR3(pPlayer->GetPlayerPed()->GetTransform().GetPosition());
Vector3 diff = playerPos - pos;
float playerDist = diff.XYMag2();
if (!pClosestPlayer || playerDist < closestDist)
{
pClosestPlayer = pPlayer;
closestDist = playerDist;
}
}
}
}
return const_cast<CNetGamePlayer*>(pClosestPlayer);
}
PlayerFlags CGameScriptHandlerNetComponent::GetParticipantFlags() const
{
PlayerFlags participantFlags = 0;
for (ScriptSlotNumber slot =0; slot<m_MaxNumParticipants; slot++)
{
const participantData* pParticipant = m_ParticipantsArray[slot];
if (pParticipant && gnetVerify(pParticipant->GetPlayer()))
{
participantFlags |= (1<<pParticipant->GetPlayer()->GetPhysicalPlayerIndex());
}
}
return participantFlags;
}
bool CGameScriptHandlerNetComponent::IsParticipantPhysical(int participantSlot, const CNetGamePlayer** ppParticipant)
{
if (scriptHandlerNetComponent::IsParticipantActive(participantSlot))
{
const CNetGamePlayer* player = static_cast<const CNetGamePlayer*>(GetParticipantUsingSlot(participantSlot));
// returns true if the participant with the given id is active, in our roaming bubble, has a player ped and has had a script join event triggered for him
if (player && player->IsPhysical() && player->GetPlayerPed() && !(m_JoinEventFlags & (1<<participantSlot)))
{
if (ppParticipant)
*ppParticipant = player;
return true;
}
}
return false;
}
#if __DEV
void CGameScriptHandlerNetComponent::DisplayScriptHandlerInfo() const
{
switch (m_State)
{
case NETSCRIPT_INTERNAL_START:
grcDebugDraw::AddDebugOutput("State: Waiting to start");
break;
case NETSCRIPT_INTERNAL_JOIN:
case NETSCRIPT_INTERNAL_JOINING:
case NETSCRIPT_INTERNAL_ACCEPTED:
case NETSCRIPT_INTERNAL_RESTARTING:
grcDebugDraw::AddDebugOutput("State: Joining");
break;
case NETSCRIPT_INTERNAL_HANDSHAKING:
grcDebugDraw::AddDebugOutput("State: Handshaking");
break;
case NETSCRIPT_INTERNAL_READY_TO_PLAY:
case NETSCRIPT_INTERNAL_PLAYING:
grcDebugDraw::AddDebugOutput("State: Playing");
break;
case NETSCRIPT_INTERNAL_LEAVE:
case NETSCRIPT_INTERNAL_LEAVING:
grcDebugDraw::AddDebugOutput("State: Leaving");
break;
case NETSCRIPT_INTERNAL_TERMINATING:
grcDebugDraw::AddDebugOutput("State: Terminating");
break;
case NETSCRIPT_INTERNAL_FINISHED:
grcDebugDraw::AddDebugOutput("State: Finished");
break;
default:
gnetAssertf(0, "Unrecognised internal script handler net component state");
}
grcDebugDraw::AddDebugOutput("Num participants: %d / %d", m_NumParticipants, m_MaxNumParticipants);
char otherPlayers[200];
otherPlayers[0] = 0;
const netPlayer* pHost = NULL;
for (u32 i=0; i<m_MaxNumParticipants; i++)
{
const netPlayer* pParticipant = GetParticipantUsingSlot(i);
if (pParticipant)
{
if (pParticipant == GetHost())
{
pHost = pParticipant;
}
else
{
int len = istrlen(otherPlayers);
if (len + strlen(pParticipant->GetGamerInfo().GetName()) <= 200)
{
if (pParticipant->IsMyPlayer())
{
formatf(otherPlayers, "%s Us(%d)", otherPlayers, GetSlotParticipantIsUsing(*pParticipant));
}
else
{
formatf(otherPlayers, "%s %s(%d)", otherPlayers, pParticipant->GetGamerInfo().GetName(), GetSlotParticipantIsUsing(*pParticipant));
}
}
}
}
}
if (IsPlayerAParticipant(*NetworkInterface::GetPlayerMgr().GetMyPlayer()))
{
grcDebugDraw::AddDebugOutput("My participant num: %d", GetSlotParticipantIsUsing(*NetworkInterface::GetPlayerMgr().GetMyPlayer()));
}
else
{
grcDebugDraw::AddDebugOutput("We are not a participant");
}
if (pHost)
{
if (pHost->IsMyPlayer())
{
grcDebugDraw::AddDebugOutput("Host: Us (%d)", GetSlotParticipantIsUsing(*pHost));
}
else
{
grcDebugDraw::AddDebugOutput("Host: %s (%d)", pHost->GetLogName(), GetSlotParticipantIsUsing(*pHost));
}
}
else
grcDebugDraw::AddDebugOutput("Host: -none-");
if (strlen(otherPlayers) > 0)
grcDebugDraw::AddDebugOutput("Other participants: %s", otherPlayers);
else
grcDebugDraw::AddDebugOutput("No other participants");
}
#endif
bool CGameScriptHandlerNetComponent::AddPlayerAsParticipant(const netPlayer& player, ScriptParticipantId participantId, ScriptSlotNumber slotNumber)
{
bool ret = true;
// we shouldn't be accepting new participants when we are migrating the host
gnetAssert(!(IsHostLocal() && m_PendingHost));
if (!IsPlayerAParticipant(player))
{
ret = scriptHandlerNetComponent::AddPlayerAsParticipant(player, participantId, slotNumber);
if (ret)
{
// we must wait until the script has a timestamp before we can add a player to the remote script info
if (m_State >= NETSCRIPT_INTERNAL_PLAYING)
{
CTheScripts::GetScriptHandlerMgr().AddPlayerToRemoteScript(m_ParentHandler.GetScriptId(), player);
}
// if we are the host, inform other non-participant players about the new participant
if (IsHostLocal())
{
m_bBroadcastScriptInfo = true;
}
// set a flag for this participant, indicating that we need to trigger a script join event for him
if (!player.IsMyPlayer())
{
m_JoinEventFlags |= (1<<slotNumber);
}
}
}
return ret;
}
void CGameScriptHandlerNetComponent::RemovePlayerAsParticipant(const netPlayer& player)
{
participantData* pParticipant = GetParticipantsData(player);
if (pParticipant)
{
// cleanup all objects created by this player
static_cast<CGameScriptHandlerNetwork*>(&m_ParentHandler)->CleanupAllClientObjects(pParticipant->GetSlotNumber());
if (!m_Terminated)
{
// only trigger a leave event if the player is physical and a join event was triggered
if (m_JoinEventFlags & (1<<pParticipant->GetSlotNumber()))
{
// no join event has been triggered yet, so just dump it
m_JoinEventFlags &= ~(1<<pParticipant->GetSlotNumber());
}
}
}
scriptHandlerNetComponent::RemovePlayerAsParticipant(player);
// we must wait until the script has a timestamp before we can remove a player to the remote script info
if (m_State >= NETSCRIPT_INTERNAL_PLAYING && m_bStartedPlaying)
{
CTheScripts::GetScriptHandlerMgr().RemovePlayerFromRemoteScript(m_ParentHandler.GetScriptId(), player, true);
}
// if we are the host, inform other non-participant players about leaving participant, unless the player is leaving the session -
// every other player will be informed of this and adjust their remote script info accordingly.
if (!player.IsLeaving() && IsHostLocal())
{
m_bBroadcastScriptInfo = true;
}
}
void CGameScriptHandlerNetComponent::HandleLeavingPlayer(const netPlayer& player, bool playerLeavingSession)
{
if (m_ParentHandler.GetThread() && IsPlayerAParticipant(player))
{
const CNetGamePlayer& gamePlayer = static_cast<const CNetGamePlayer&>(player);
// special leave type
CEventNetworkPlayerLeftScript::eSource nSource = CEventNetworkPlayerLeftScript::SOURCE_NORMAL;
if(gamePlayer.HasStartedTransition())
nSource = CEventNetworkPlayerLeftScript::SOURCE_TRANSITION;
else if(CNetwork::GetNetworkSession().DidGamerLeaveForStore(gamePlayer.GetGamerInfo().GetGamerHandle()))
nSource = CEventNetworkPlayerLeftScript::SOURCE_STORE;
// find an existing event to collate, or generate a new event
if (!GetEventScriptNetworkGroup()->CollateLeftScriptEventForPlayer(player, nSource, m_ParentHandler.GetThread()->GetThreadId()))
{
CEventNetworkPlayerLeftScript leaveEvent(m_ParentHandler.GetThread()->GetThreadId(), gamePlayer, nSource, gamePlayer.GetSessionBailReason());
GetEventScriptNetworkGroup()->Add(leaveEvent);
}
}
scriptHandlerNetComponent::HandleLeavingPlayer(player, playerLeavingSession);
}
void CGameScriptHandlerNetComponent::SetNewScriptHost(const netPlayer* pPlayer, HostToken hostToken, bool bBroadcast)
{
CGameScriptId* pHandlerId = static_cast<CGameScriptId*>(&m_ParentHandler.GetScriptId());
if (pPlayer)
{
gnetAssert(hostToken != INVALID_HOST_TOKEN);
if (pHandlerId->GetTimeStamp() == 0)
{
if (pPlayer && pPlayer->IsLocal())
{
unsigned netTime = NetworkInterface::GetNetworkTime();
// if the net time is 0, we have to timestamp with a non-zero value
if (!Verifyf(netTime != 0, "Trying to timestamp a script with a network time of 0! Timestamping with a random number instead"))
{
netTime = fwRandom::GetRandomNumber();
}
// we are the first host and so the script has just started. Stamp the current time into the network id to make it unique.
pHandlerId->SetTimeStamp(netTime);
}
else
{
// set the timestamp received from the host
gnetAssert(m_PendingTimestamp != 0);
pHandlerId->SetTimeStamp(m_PendingTimestamp);
m_PendingTimestamp = 0;
}
}
}
scriptHandlerNetComponent::SetNewScriptHost(pPlayer, hostToken, bBroadcast);
if (GetHost() && m_State >= NETSCRIPT_INTERNAL_READY_TO_PLAY && m_State < NETSCRIPT_INTERNAL_LEAVE)
{
if (m_State == NETSCRIPT_INTERNAL_PLAYING && m_ParentHandler.GetThread())
{
CEventNetworkHostMigration migrateEvent(m_ParentHandler.GetThread()->GetThreadId(), *static_cast<const CNetGamePlayer*>(pPlayer));
GetEventScriptNetworkGroup()->Add(migrateEvent);
}
if (gnetVerify(m_HostData))
{
CTheScripts::GetScriptHandlerMgr().SetNewHostOfRemoteScript(m_ParentHandler.GetScriptId(), *pPlayer, hostToken, GetParticipantFlags());
}
if (IsHostLocal())
{
// inform other non-participant players that we are the new host
m_bBroadcastScriptInfo = true;
// remove any script objects created by the previous host after we got the last host broadcast data update
static_cast<CGameScriptHandlerNetwork&>(m_ParentHandler).RemoveOldHostObjects();
}
}
}
bool CGameScriptHandlerNetComponent::CanStartJoining()
{
// wait for the script to finish its setup
if (!m_bScriptReady)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Can't start joining - waiting on script");
return false;
}
if (!NetworkInterface::IsGameInProgress())
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Can't start joining - network game not in progress");
return false;
}
if (!NetworkInterface::IsSessionEstablished())
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Can't start joining - the session has not started");
return false;
}
// we need to wait until we are accepted into a roaming bubble before proceeding
if (!NetworkInterface::GetLocalPlayer()->IsInRoamingBubble())
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Can't start joining - waiting on roaming bubble");
return false;
}
return scriptHandlerNetComponent::CanStartJoining();
}
void CGameScriptHandlerNetComponent::RestartJoiningProcess()
{
// we have to send out a leave message here if we have been accepted by the previous host, as he may have sent out a remote
// script info broadcast with a different timestamp on it and the other machines must know we have left so they know to move the
// remote script info onto the terminated list
if (IsPlayerAParticipant(*NetworkInterface::GetLocalPlayer()))
{
PlayerFlags participants = GetParticipantFlags();
CGameScriptId scrId(static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()));
if (scrId.GetTimeStamp() == 0)
{
scrId.SetTimeStamp(m_PendingTimestamp);
}
// send a leave msg to all non-participants
CRemoteScriptLeaveEvent::Trigger(scrId, ~participants);
}
static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()).ResetTimestamp();
scriptHandlerNetComponent::RestartJoiningProcess();
m_PendingTimestamp = 0;
m_JoinEventFlags = 0;
}
void CGameScriptHandlerNetComponent::StartPlaying()
{
CGameScriptHandlerNetwork& gameHandler = static_cast<CGameScriptHandlerNetwork&>(m_ParentHandler);
scriptHandlerNetComponent::StartPlaying();
if (m_ParentHandler.GetThread())
{
static_cast<GtaThread*>(m_ParentHandler.GetThread())->m_NetComponent = this;
}
gameHandler.RegisterAllExistingScriptObjects();
gameHandler.NetworkGameStarted();
// we should have been given a timestamp by the host at this point
gnetAssert(static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()).GetTimeStamp() != 0);
// move a currently active script matching this one (but with an older timestamp) onto the terminated queue (there may be one if we received a remote script info from a
// previous host who left during the joining process)
CTheScripts::GetScriptHandlerMgr().TerminateActiveScriptInfo(static_cast<CGameScriptId&>(gameHandler.GetScriptId()));
CGameScriptHandlerNetwork& parentHandler = static_cast<CGameScriptHandlerNetwork&>(m_ParentHandler);
CGameScriptHandlerMgr::CRemoteScriptInfo scriptInfo(static_cast<CGameScriptId&>(parentHandler.GetScriptId()),
GetParticipantFlags(),
m_HostToken,
GetHost(),
parentHandler.GetNumGlobalReservedPeds(),
parentHandler.GetNumGlobalReservedVehicles(),
parentHandler.GetNumGlobalReservedObjects());
scriptInfo.SetRunningLocally(true);
CTheScripts::GetScriptHandlerMgr().AddRemoteScriptInfo(scriptInfo);
// if we are the host, inform other non-participant players about the new script session
if (IsHostLocal())
{
m_bBroadcastScriptInfo = true;
}
m_bDoneLeaveCleanup = false;
m_bDoneTerminationCleanup = false;
m_bStartedPlaying = true;
}
bool CGameScriptHandlerNetComponent::DoLeaveCleanup()
{
// DoLeaveCleanup may get called multiple times, hence m_bDoneLeaveCleanup flag
if (NetworkInterface::IsGameInProgress())
{
if (!m_bDoneLeaveCleanup)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Mission Finished: %s", m_ScriptFinished ? "true" : "false");
if (IsHostLocal())
{
if (m_ScriptFinished || (m_NumParticipants == 1 && m_State > NETSCRIPT_INTERNAL_PLAYING))
{
// update our remote script info with 0 participants to indicate that it is finished
CGameScriptHandlerMgr::CRemoteScriptInfo scriptInfo(static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()), 0, m_HostToken, GetHost(), 0, 0, 0);
CTheScripts::GetScriptHandlerMgr().AddRemoteScriptInfo(scriptInfo);
}
// inform all non-participants that the script has terminated
m_bBroadcastScriptInfo = true;
}
// remove all script peds from our local player's group
CPed* pLocalPlayer = CGameWorld::FindLocalPlayer();
CPedGroup* pPlayerGroup = pLocalPlayer ? pLocalPlayer->GetPedsGroup() : NULL;
if (pPlayerGroup)
{
CPedGroupMembership* pMembership = pPlayerGroup->GetGroupMembership();
if (pMembership->CountMembers() > 1)
{
for (int i=0; i<CPedGroupMembership::MAX_NUM_MEMBERS; i++)
{
CPed* pMember = pMembership->GetMember(i);
if (pMember && !pMembership->IsLeader(pMember) && AssertVerify(pMember != pLocalPlayer))
{
CScriptEntityExtension* pExtension = pMember->GetExtension<CScriptEntityExtension>();
if (pExtension && pExtension->GetScriptInfo() && pExtension->GetScriptInfo()->GetScriptId() == m_ParentHandler.GetScriptId())
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Removing %s from local player's group", pMember->GetNetworkObject() ? pMember->GetNetworkObject()->GetLogName() : "??");
pMembership->RemoveMember(pMember);
}
}
}
}
}
m_bDoneLeaveCleanup = true;
}
}
return scriptHandlerNetComponent::DoLeaveCleanup();
}
bool CGameScriptHandlerNetComponent::DoTerminationCleanup()
{
// DoTerminationCleanup may get called multiple times, hence m_bDoneTerminationCleanup flag
if (NetworkInterface::IsGameInProgress())
{
if (!m_bDoneTerminationCleanup)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), m_ParentHandler.GetScriptId(), "Mission Finished: %s", m_ScriptFinished ? "true" : "false");
// send a leave msg to all non-participants
if (IsPlayerAParticipant(*NetworkInterface::GetLocalPlayer()))
{
PlayerFlags participants = GetParticipantFlags();
CTheScripts::GetScriptHandlerMgr().WriteScriptHeader(CTheScripts::GetScriptHandlerMgr().GetLog(), m_ParentHandler.GetScriptId(), "Broadcasting remote script leave event. Players: %u", ~participants);
CGameScriptId scrId(static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()));
if (scrId.GetTimeStamp() == 0)
{
scrId.SetTimeStamp(m_PendingTimestamp);
}
CRemoteScriptLeaveEvent::Trigger(scrId, ~participants);
if (m_State >= NETSCRIPT_INTERNAL_PLAYING && m_bStartedPlaying)
{
CTheScripts::GetScriptHandlerMgr().RemovePlayerFromRemoteScript(m_ParentHandler.GetScriptId(), *NetworkInterface::GetLocalPlayer(), true);
}
}
#if __BANK
if (GetNumHostBroadcastDatasRegistered() || GetNumPlayerBroadcastDatasRegistered())
{
netLoggingInterface* log = scriptInterface::GetScriptManager().GetLog();
if (log)
{
NetworkLogUtils::WriteLogEvent(*log, "BANDWIDTH_STATS", "%s", m_ParentHandler.GetScriptId().GetLogName());
log->WriteDataValue("Total in", "%d", GetTotalBandwidthIn());
log->WriteDataValue("Total out", "%d", GetTotalBandwidthOut());
log->WriteDataValue("Average in", "%d", GetAverageBandwidthIn());
log->WriteDataValue("Average out", "%d", GetAverageBandwidthOut());
log->WriteDataValue("Peak in", "%d", GetPeakBandwidthIn());
log->WriteDataValue("Peak out", "%d", GetPeakBandwidthOut());
}
char dvName[100];
for (int i=0; i<GetNumHostBroadcastDatasRegistered(); i++)
{
netScriptBroadcastDataHandlerBase *hostHandler = GetHostBroadcastDataHandler(i);
if (gnetVerify(hostHandler))
{
formatf(dvName, "Host array handler %d", i);
log->WriteDataValue(dvName, "Total out: %d, Avg out: %d, Peak out: %d", hostHandler->GetTotalBandwidthOut(), hostHandler->GetAverageBandwidthOut(GetTimeHandlerActive()), hostHandler->GetPeakBandwidthOut());
}
}
for (int i=0; i<GetNumPlayerBroadcastDatasRegistered(); i++)
{
netScriptBroadcastDataHandlerBase *playerHandler = GetLocalPlayerBroadcastDataHandler(i);
if (gnetVerify(playerHandler))
{
formatf(dvName, "Player array handler %d", i);
log->WriteDataValue(dvName, "Total out: %d, Avg out: %d, Peak out: %d", playerHandler->GetTotalBandwidthOut(), playerHandler->GetAverageBandwidthOut(GetTimeHandlerActive()), playerHandler->GetPeakBandwidthOut());
}
}
}
#endif // __BANK
m_bDoneTerminationCleanup = true;
}
// we must wait until all important objects have migrated before we can terminate the handler
if (!m_ScriptFinished && static_cast<CGameScriptHandlerNetwork&>(m_ParentHandler).MigrateObjects())
{
return false;
}
}
return scriptHandlerNetComponent::DoTerminationCleanup();
}
bool CGameScriptHandlerNetComponent::BroadcastRemoteScriptInfo(const netPlayer* pPlayer)
{
if (NetworkInterface::IsGameInProgress() && m_State >= NETSCRIPT_INTERNAL_READY_TO_PLAY)
{
// TODO: add for bots? Ignore for now
if ((!pPlayer || !pPlayer->IsBot()) &&
gnetVerify(IsHostLocal()) &&
gnetVerify(static_cast<CGameScriptId&>(m_ParentHandler.GetScriptId()).GetTimeStamp() != 0))
{
PlayerFlags participants = GetParticipantFlags();
// send out 0 participants if the script is finishing or we are leaving and are the only machine running the script.
// This informs everyone that the script has definitely terminated.
if (m_ScriptFinished || (m_NumParticipants == 1 && m_State > NETSCRIPT_INTERNAL_PLAYING))
participants = 0;
// remove our player if we are leaving the script
if (m_State > NETSCRIPT_INTERNAL_PLAYING)
participants &= ~(1<<NetworkInterface::GetLocalPhysicalPlayerIndex());
CTheScripts::GetScriptHandlerMgr().WriteScriptHeader(CTheScripts::GetScriptHandlerMgr().GetLog(), m_ParentHandler.GetScriptId(), "Broadcast script info. Participants: %d. Our host token: %d", participants, GetHostToken());
// send to all non-participants unless we want to send to a specific player
PlayerFlags playersToSendTo = 0;
if (pPlayer)
{
if (AssertVerify(!pPlayer->IsLocal()))
{
playersToSendTo = (1<<pPlayer->GetPhysicalPlayerIndex());
}
}
else
{
playersToSendTo = ~participants & NetworkInterface::GetPlayerMgr().GetRemotePhysicalPlayersBitmask();
playersToSendTo &= ~(1<<NetworkInterface::GetLocalPhysicalPlayerIndex());
}
if (playersToSendTo != 0)
{
CGameScriptHandlerNetwork& parentHandler = static_cast<CGameScriptHandlerNetwork&>(m_ParentHandler);
CGameScriptHandlerMgr::CRemoteScriptInfo scriptInfo(static_cast<CGameScriptId&>(parentHandler.GetScriptId()),
participants,
m_HostToken,
GetHost(),
parentHandler.GetNumGlobalReservedPeds(),
parentHandler.GetNumGlobalReservedVehicles(),
parentHandler.GetNumGlobalReservedObjects());
if (!CRemoteScriptInfoEvent::Trigger(scriptInfo, playersToSendTo))
{
return false;
}
}
}
}
return true;
}