Files
GTASource/game/network/Network.cpp

6741 lines
245 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
//
// name: network.cpp
// description: High level interface to network stuff. All external access to the network code
// must go through this class.
// written by: John Gurney
//
#include "network/Network.h"
#include "network/Cloud/Tunables.h"
// rage headers
#include "data/rson.h"
#include "diag/seh.h"
#include "file/device_installer.h"
#include "net/net.h"
#include "net/connectionrouter.h"
#include "net/keyexchange.h"
#include "net/natdetector.h"
#include "net/netallocator.h"
#include "net/netfuncprofiler.h"
#include "net/timesync.h"
#include "net/tunneler_ice.h"
#include "rline/rl.h"
#include "rline/scmatchmaking/rlscmatchmaking.h"
#include "rline/scmatchmaking/rlscmatchmanager.h"
#include "rline/scmembership/rlscmembership.h"
#include "rline/scpresence/rlscpresence.h"
#include "rline/scpresence/rlscpresencetasks.h"
#include "rline/rltitleid.h"
#include "string/stringutil.h"
#include "system/memmanager.h"
#include "system/param.h"
#include "system/simpleallocator.h"
// framework headers
#include "fwlocalisation/languagePack.h"
#include "fwanimation/directorcomponentsyncedscene.h"
#include "fwnet/NetLogUtils.h"
#include "fwnet/NetPlayerMessages.h"
#include "fwnet/netchannel.h"
#include "fwnet/NetEventMgr.h"
#include "fwnet/netremotelog.h"
#include "fwnet/netscgamerdatamgr.h"
#include "fwnet/netutils.h"
#include "fwrenderer/renderthread.h"
#include "fwscene/stores/staticboundsstore.h"
#include "net/nethardware.h"
#include "streaming/packfilemanager.h"
// game headers
#include "audio/northaudioengine.h"
#include "audio/radiostation.h"
#include "camera/CamInterface.h"
#include "camera/gameplay/follow/FollowVehicleCamera.h"
#include "control/gamelogic.h"
#include "core/app.h"
#include "core/game.h"
#include "cutscene/CutSceneManagerNew.h"
#include "event/EventDamage.h"
#include "event/EventGroup.h"
#include "event/EventNetwork.h"
#include "frontend/MiniMap.h"
#include "Frontend/MultiplayerGamerTagHud.h"
#include "Frontend/MultiplayerChat.h"
#include "game/BackgroundScripts/BackgroundScripts.h"
#include "game/weather.h"
#include "network/arrays/NetworkArrayMgr.h"
#include "network/Bandwidth/NetworkBandwidthManager.h"
#include "network/Cloud/UserContentManager.h"
#include "network/Crews/NetworkCrewDataMgr.h"
#include "network/Debug/NetworkBot.h"
#include "network/Debug/NetworkDebug.h"
#include "network/Debug/NetworkSoakTests.h"
#include "network/events/NetworkEventTypes.h"
#include "Network/General/NetworkAssetVerifier.h"
#include "network/general/NetworkFakePlayerNames.h"
#include "Network/General/NetworkHasherUtil.h"
#include "Network/General/NetworkScratchpad.h"
#include "network/general/NetworkStreamingRequestManager.h"
#include "network/Live/livemanager.h"
#include "Network/Live/NetworkTelemetry.h"
#include "Network/Objects/NetworkObjectMgr.h"
#include "Network/Objects/NetworkObjectPopulationMgr.h"
#include "Network/Objects/Entities/NetObjPlayer.h"
#include "Network/Party/NetworkParty.h"
#include "network/players/NetworkPlayerMgr.h"
#include "network/roaming/RoamingBubbleMgr.h"
#include "Network/Sessions/NetworkSession.h"
#include "Network/Sessions/NetworkVoiceSession.h"
#include "Network/Shop/Catalog.h"
#include "Network/SocialClubServices/GamePresenceEvents.h"
#include "Network/Stats/networkleaderboardsessionmgr.h"
#include "Network/Voice/NetworkVoice.h"
#include "Network/SocialClubServices/SocialClubCommunityEventMgr.h"
#include "network/Live/NetworkLegalRestrictionsManager.h"
#include "peds/PedPopulation.h"
#include "peds/Ped.h"
#include "peds/PedIntelligence.h"
#include "peds/PlayerInfo.h"
#include "pickups/PickupManager.h"
#include "scene/DataFileMgr.h"
#include "scene/FileLoader.h"
#include "scene/streamer/StreamVolume.h"
#include "scene/texlod.h"
#include "scene/world/GameWorld.h"
#include "script/commands_datafile.h"
#include "script/commands_network.h"
#include "script/script_hud.h"
#include "streaming/IslandHopper.h"
#include "streaming/PopulationStreaming.h"
#include "system/controlMgr.h"
#include "system/memvisualize.h"
#include "system/memmanager.h"
#include "system/InfoState.h"
#include "vehicles/vehiclepopulation.h"
#include "task/Physics/TaskNM.h"
#include "task/Combat/TaskCombatMelee.h"
#include "task/Combat/Subtasks/TaskVehicleCombat.h"
#include "task/Default/TaskIncapacitated.h"
#include "task/Default/TaskPlayer.h"
#include "Task/Motion/Locomotion/TaskInWater.h"
#include "task/Movement/TaskGetUp.h"
#include "task/Scenario/Types/TaskUseScenario.h"
#include "task/Weapons/WeaponTarget.h"
#include "task/Vehicle/TaskExitVehicle.h"
#include "text/messages.h"
#include "Vehicles/Bike.h"
#include "Vehicles/cargen.h"
#include "Vehicles/train.h"
#include "vehicles/vehicle.h"
#include "vfx/misc/MovieManager.h"
#include "vfx/vehicleglass/VehicleGlassComponent.h"
#include "Weapons/Projectiles/Projectile.h"
#include "Weapons/Projectiles/ProjectileManager.h"
#include "weapons/Bullet.h"
#include "weapons/Weapon.h"
#include "stats/StatsInterface.h"
#include "stats/StatsDataMgr.h"
#include "stats/StatsMgr.h"
#include "stats/StatsSavesMgr.h"
#include "Objects/Door.h"
#include "stats/MoneyInterface.h"
#include "renderer/RenderTargetMgr.h"
#include "vehicles/vehicleDamage.h"
#include "frontend/UIWorldIcon.h"
#include "frontend/GameStreamMgr.h"
#include "frontend/WarningScreen.h"
//ORBIS
#if RSG_ORBIS
#include <game_live_streaming.h>
#include <libsysmodule.h>
#pragma comment(lib,"SceGameLiveStreaming_stub_weak")
// Provided by Sony to specifically detect if we're running in back-compat mode
#include "system/kernel_cpumode_platform.h"
#pragma comment(lib,"kernel_cpumode_platform_stub_weak")
#endif
#if !__FINAL && RSG_ORBIS
#include <libdbg.h>
#endif
#if RSG_PC
#include "net/natdetector.h"
#include "net/natpcp.h"
#include "net/natupnp.h"
#include "net/tunneler_ice.h"
#endif
NETWORK_OPTIMISATIONS()
RAGE_DECLARE_CHANNEL(net);
#undef __net_channel
#define __net_channel net
// these prevent spam in the net channel
RAGE_DEFINE_SUBCHANNEL(net, tod, DIAG_SEVERITY_DEBUG3, DIAG_SEVERITY_WARNING, DIAG_SEVERITY_ASSERT)
#define netTodDebug(fmt, ...) RAGE_DEBUGF1(net_tod, fmt, ##__VA_ARGS__)
#define netTodError(fmt, ...) RAGE_ERRORF(net_tod, fmt, ##__VA_ARGS__)
#define netTodVerify(cond) RAGE_VERIFY(net_tod, cond)
// these prevent spam in the net channel
RAGE_DEFINE_SUBCHANNEL(net, cxnevent, DIAG_SEVERITY_DEBUG3, DIAG_SEVERITY_WARNING, DIAG_SEVERITY_ASSERT)
#define netCxnEventDebug(fmt, ...) RAGE_DEBUGF1(net_cxnevent, fmt, ##__VA_ARGS__)
RAGE_DEFINE_SUBCHANNEL(net, transition, DIAG_SEVERITY_DEBUG3)
#define netTransitionDebug(fmt, ...) RAGE_DEBUGF1(net_transition, fmt, ##__VA_ARGS__)
#define netTransitionAssertf(cond, fmt, ...) RAGE_ASSERTF(net_transition,cond, fmt, ##__VA_ARGS__)
// The Network update function can be called recursively during the loading
// We don't profile functions during loading to avoid pushing too many profilers
#define UPDATE_PROFILE(_functionName) if(fromLoadingCode) { _functionName; } else { NPROFILE(_functionName); }
#define UPDATE_PROFILE_BLOCK(_blockName) NPROFILE_BLOCK_COND(_blockName, fromLoadingCode);
// rage default is rage::netConnectionPolicies::DEFAULT_TIMEOUT_INTERVAL, which is different than this one. Timer can change via cloud
int CNetwork::DEFAULT_CONNECTION_TIMEOUT = netConnectionPolicies::DEFAULT_TIMEOUT_INTERVAL;
int CNetwork::DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL = netConnectionPolicies::DEFAULT_KEEP_ALIVE_INTERVAL;
static bool s_EnableCodeBailAlerting = false;
namespace rage
{
extern sysMemAllocator* g_rlAllocator;
extern const rlTitleId* g_rlTitleId;
XPARAM(nopopups);
}
//static const unsigned MAX_VOICE_BANDWIDTH = 16*1024/8; //kbps
NOSTRIP_FINAL_LOGGING_PARAM(socketport, "[network] 8-bit network port on which to communicate");
PARAM(notimeouts, "[network] Disable timeouts for network sessions");
PARAM(timeout, "[network] Sets the timeout period to a number of seconds");
PARAM(nonetwork, "[network] No network");
PARAM(netForceCheater, "[network] Force local player cheater setting (0 or 1)");
PARAM(nompvehicles, "[network] Don't switch to the lower-LOD vehicles in MP");
PARAM(disableLeavePed, "[network] Disable leave ped behind when a player leaves the session");
PARAM(netFunctionProfilerDisabled, "[network] Disables netFunctionProfiler");
PARAM(netFunctionProfilerAsserts, "[network] Asserts when function profiler exceeds supplied times");
PARAM(netFunctionProfilerThreshold, "[network] Only outputs function that took more than supplied ms");
PARAM(netSendStallTelemetry, "[network] Only outputs function that took more than supplied ms");
PARAM(netDisableLoggingStallTracking, "[network] Disables functionality to track logging stalls");
XPARAM(lan);
PARAM(netBypassMultiplayerAccessChecks, "[network] Bypass soft network access blockers");
PARAM(netRequireSpPrologue, "[network] Force a setting for requiring the SP prologue");
PARAM(netSpPrologueComplete, "[network] Force that the SP prologue is complete");
PARAM(netMpSkipCloudFileRequirements, "[session] If present, skips need for tunables or background scripts");
XPARAM(mpavailable);
#if RSG_XBOX
PARAM(netXblUseConnectivityForLinkConnected, "[network] Whether we use the network connectivity to drive whether we consider the cable connected");
#endif
PARAM(netObjectIdRequestIdThreshold, "[network] Object Id request Id release threshold");
PARAM(netObjectIdNumIdsToRequest, "[network] Object Id number of Ids that we request when ");
PARAM(netObjectIdMinTimeBetweenRequests, "[network] Object Id minimum time between Id requests");
PARAM(netObjectIdObjectIdReleaseThreshold, "[network] Object Id release threshold");
// starting position vector in the network.
PARAM(netStartPos, "[network] Set the player start position.");
// starting team in the network.
PARAM(netTeam, "[network] Set the player's team.");
PARAM(netHttpRequestTrackerDisplayOnScreenMessage, "[network] The HTTP request tracker will display a message on screen if there is a burst.");
XPARAM(mapMoverOnlyInMultiplayer);
PARAM(netNoScAuth, "Disables ScAuth signup and account linking");
PARAM(netScAuthNoExit, "Disable exiting the ScAuth flow through BACK button, requires browser javascript exit");
PARAM(netTestTunableConnectionMgrHeapSwap, "Tests swapping out the heap at the point of loading tunables");
#if RSG_ORBIS
PARAM(netForceBackCompatAsPS5, "[network] Force acting as playing on PS5 with back compat", "Disabled", "PS4", "");
PARAM(PS4BackwardsCompatibility, "Force acting as playing on PS5 with back compat", "Disabled", "PS4", "");
#endif
#if __BANK
PARAM(enableSaveMigration, "[network] Enable Single Player savegame migration even if the Tunable is not set");
#endif // __BANK
// Network Allocator
static __THREAD sysMemAllocator* s_NetworkAllocator;
static __THREAD s32 s_heapStackSize;
// Stall detection tunables
static const unsigned DEFAULT_LONG_FRAME_TIME = 100;
static unsigned s_nLongFrameTime = DEFAULT_LONG_FRAME_TIME;
static const unsigned DEFAULT_STALL_DETECTION_SP = 1000;
static const unsigned DEFAULT_STALL_DETECTION_MP = 1000;
static unsigned s_nTimeForLongStallTelemetrySP = DEFAULT_STALL_DETECTION_SP;
static unsigned s_nTimeForLongStallTelemetryMP = DEFAULT_STALL_DETECTION_MP;
static const unsigned DEFAULT_ENDPOINT_POOL_HINT = MAX_NUM_ACTIVE_PLAYERS;
static const unsigned DEFAULT_CXN_POOL_HINT = DEFAULT_ENDPOINT_POOL_HINT * 2;
//bail
struct AlertParameters
{
AlertParameters()
: m_IsActive(false)
, m_ResolveRequiresOnline(false)
, m_InstanceHash(0)
{
}
sNetworkErrorDetails m_ErrorDetails;
bool m_IsActive;
bool m_ResolveRequiresOnline;
unsigned m_InstanceHash;
void SetActive()
{
m_IsActive = true;
}
bool HasActiveMessage() const
{
return m_IsActive;
}
void ClearMessage()
{
m_ResolveRequiresOnline = false;
m_InstanceHash = 0;
m_IsActive = false;
}
};
static AlertParameters s_NetworkAlertParams;
void sysMemStartNetwork()
{
gnetAssertf(CNetwork::GetNetworkHeap(), "Network heap doesn't exist");
gnetAssertf(s_heapStackSize >= 0, "Invalid Network heap stack size: %d", s_heapStackSize);
if (CNetwork::GetNetworkHeap() && s_heapStackSize == 0)
{
if (CNetwork::GetNetworkHeap() == &sysMemAllocator::GetCurrent())
{
Quitf(ERR_NET_MEM,"Network Heap is already in use! This is very bad and we will crash now...");
}
s_NetworkAllocator = &sysMemAllocator::GetCurrent();
sysMemAllocator::SetCurrent(*CNetwork::GetNetworkHeap());
}
s_heapStackSize++;
}
void sysMemEndNetwork()
{
gnetAssertf(CNetwork::GetNetworkHeap(), "Network heap doesn't exist");
gnetAssertf(s_heapStackSize > 0, "Invalid Network heap stack size: %d", s_heapStackSize);
s_heapStackSize--;
if (CNetwork::GetNetworkHeap() && s_heapStackSize == 0)
{
sysMemAllocator::SetCurrent(*s_NetworkAllocator);
s_NetworkAllocator = NULL;
}
}
//PURPOSE
// Private class supporting game level queries from the framework level
class NetQueryFunctions : public netInterface::queryFunctions
{
public:
NetQueryFunctions() {}
~NetQueryFunctions() {}
bool IsSnSessionEstablished() const
{
return CNetwork::GetNetworkSession().GetSnSession().IsEstablished();
}
bool IsSessionEstablished() const
{
return CNetwork::GetNetworkSession().IsSessionEstablished();
}
unsigned GetNetworkTime()
{
return CNetwork::GetNetworkTime();
}
void FlushAllLogFiles(bool waitForFlush)
{
return CNetwork::FlushAllLogFiles(waitForFlush);
}
};
#if RSG_PC
NetworkExitFlow::NetworkExitFlow()
: m_ExitToDesktopState(ETD_NONE)
, m_fromMP(false)
{
}
bool NetworkExitFlow::IsExitFlowRunning()
{
return m_ExitToDesktopState != ETD_NONE;
}
void NetworkExitFlow::StartExitSaveFlow()
{
gnetDebug1("Requested Exit MP to Desktop (Save Flow)");
if(gnetVerifyf(!IsSaveFlowRunning() && !IsShutdownTasksRunning(), "Exit to desktop flow is already running"))
{
if(NetworkInterface::IsGameInProgress())
{
SetShutdownState(ETD_WAITING_FOR_SAVE_READY);
StatsInterface::SetNoRetryOnFail(true);
m_fromMP=true;
}
}
}
bool NetworkExitFlow::IsSaveFlowRunning()
{
return m_ExitToDesktopState == ETD_WAITING_FOR_SAVE_FINISHED || m_ExitToDesktopState == ETD_WAITING_FOR_SAVE_READY;
}
bool NetworkExitFlow::IsSaveFlowFinished()
{
return m_ExitToDesktopState == ETD_SAVE_SUCCESS || m_ExitToDesktopState == ETD_SAVE_FAILED;
}
void NetworkExitFlow::StartShutdownTasks()
{
gnetDebug1("Requested Exit MP to Desktop (Shutdown Tasks)");
if(gnetVerifyf(!IsShutdownTasksRunning(), "Shutdown tasks are already running"))
{
// Don't attempt to execute NetworkExit tasks with -nonetwork
if (PARAM_nonetwork.Get())
{
SetShutdownState(ETD_READY_TO_SHUTDOWN);
}
else
{
SetShutdownState(ETD_SHUTDOWN_START);
if(g_rlPc.GetUiInterface())
{
const int SHUTDOWN_TIMEOUT_MS = (3 * 1000);
g_rlPc.GetUiInterface()->BeginShutdown(SHUTDOWN_TIMEOUT_MS);
}
}
m_fromMP = NetworkInterface::IsGameInProgress();
}
}
bool NetworkExitFlow::IsShutdownTasksRunning()
{
return m_ExitToDesktopState >= ETD_SHUTDOWN_START && m_ExitToDesktopState < ETD_READY_TO_SHUTDOWN;
}
bool NetworkExitFlow::IsReadyToShutdown()
{
return m_ExitToDesktopState == ETD_READY_TO_SHUTDOWN;
}
NetworkExitFlow::ExitToDesktopState NetworkExitFlow::GetExitFlowState()
{
return m_ExitToDesktopState;
}
void NetworkExitFlow::ResetExitFlowState()
{
gnetDebug1("Reset Exit to desktop");
gnetAssertf(m_ExitToDesktopState == ETD_NONE || IsSaveFlowFinished() || IsReadyToShutdown(), "Exit to desktop flow is not finished yet");
SetShutdownState(ETD_NONE);
}
void NetworkExitFlow::SetShutdownState(ExitToDesktopState s)
{
#if !__NO_OUTPUT
static const char *s_StateNames[] =
{
"ETD_NONE",
"ETD_WAITING_FOR_SAVE_READY",
"ETD_WAITING_FOR_SAVE_FINISHED",
"ETD_SAVE_FAILED",
"ETD_SAVE_SUCCESS",
"ETD_SHUTDOWN_START",
"ETD_FLUSH_TELEMETRY",
"ETD_UNADVERTISE_MM",
"ETD_UNADVERTISING",
"ETD_SESSION_SHUTDOWNS",
"ETD_SC_SIGNOUT",
"ETD_WAITING_FOR_SC_SIGNOUT",
"ETD_WAITING_FOR_UPNP_DELETE",
"ETD_WAITING_FOR_PCP_DELETE",
"ETD_WAITING_FOR_SCUI_SHUTDOWN",
"ETD_READY_TO_SHUTDOWN"
};
CompileTimeAssert(COUNTOF(s_StateNames) == ETD_READY_TO_SHUTDOWN + 1);
gnetDebug1("SetShutdownState(%s)", s_StateNames[(int)s]);
#endif
m_ExitToDesktopState = s;
}
void NetworkExitFlow::Update()
{
if(m_ExitToDesktopState!=ETD_NONE)
{
if(!StatsInterface::SavePending() &&
!StatsInterface::PendingSaveRequests() &&
!CLiveManager::GetProfileStatsMgr().PendingFlush() &&
!CLiveManager::GetProfileStatsMgr().PendingMultiplayerFlushRequest())
{
if(m_ExitToDesktopState == ETD_WAITING_FOR_SAVE_READY)
{
StatsInterface::Save(STATS_SAVE_CLOUD, STAT_MP_CATEGORY_DEFAULT, STAT_SAVETYPE_IMMEDIATE_FLUSH, 0);
SetShutdownState(ETD_WAITING_FOR_SAVE_FINISHED);
}
else if(m_ExitToDesktopState == ETD_WAITING_FOR_SAVE_FINISHED)
{
if(StatsInterface::CloudSaveFailed(STAT_MP_CATEGORY_DEFAULT) || CLiveManager::GetProfileStatsMgr().FlushFailed())
{
SetShutdownState(ETD_SAVE_FAILED);
StatsInterface::ClearSaveFailed(STAT_MP_CATEGORY_DEFAULT);
StatsInterface::SetNoRetryOnFail(false);
}
else
{
SetShutdownState(ETD_SAVE_SUCCESS);
StatsInterface::SetNoRetryOnFail(false);
}
}
}
if (m_ExitToDesktopState == ETD_SHUTDOWN_START)
{
// deleting the uPnP mapping can take several seconds and can run in parallel with other tasks
netNatUpNp::DeleteUpNpPortForwardRule(&m_UpNpStatus);
netNatPcp::DeletePcpAddressReservation(&m_PcpStatus);
if(CNetworkTelemetry::FlushTelemetry(true))
{
SetShutdownState(ETD_FLUSH_TELEMETRY);
}
else SetShutdownState(ETD_UNADVERTISE_MM);
}
else if (m_ExitToDesktopState == ETD_FLUSH_TELEMETRY)
{
if (!rlTelemetry::FlushInProgress())
{
SetShutdownState(ETD_UNADVERTISE_MM);
}
}
else if (m_ExitToDesktopState == ETD_UNADVERTISE_MM)
{
if (rlScMatchManager::HasAnyMatches())
{
if(gnetVerifyf(rlScMatchmaking::UnadvertiseAll(0, &m_MyStatus), "Unadvertise matchmaking failed"))
{
SetShutdownState(ETD_UNADVERTISING);
}
else SetShutdownState(ETD_SESSION_SHUTDOWNS);
}
else SetShutdownState(ETD_SESSION_SHUTDOWNS);
}
else if (m_ExitToDesktopState == ETD_UNADVERTISING)
{
if (!m_MyStatus.Pending())
{
SetShutdownState(ETD_SESSION_SHUTDOWNS);
}
}
else if (m_ExitToDesktopState == ETD_SESSION_SHUTDOWNS)
{
// Let others know we're leaving
CNetwork::Bail(sBailParameters(BAIL_EXIT_GAME, BAIL_CTX_EXIT_GAME_FROM_FLOW));
// Cancel all non essential tasks for shutdown
rlGetTaskManager()->ShutdownCancel();
netTask::CancelAll();
SetShutdownState(ETD_SC_SIGNOUT);
}
else if (m_ExitToDesktopState == ETD_SC_SIGNOUT)
{
rlScSignOutTask* task = NULL;
const int localGamerIndex = NetworkInterface::GetLocalGamerIndex();
if (NetworkInterface::IsLocalPlayerSignedIn() &&
rlGetTaskManager()->CreateTask(&task) &&
rlTaskBase::Configure(task, localGamerIndex, &m_MyStatus) &&
rlGetTaskManager()->AddParallelTask(task))
{
SetShutdownState(ETD_WAITING_FOR_SC_SIGNOUT);
}
else
{
if (task != NULL)
{
rlGetTaskManager()->DestroyTask(task);
}
SetShutdownState(ETD_WAITING_FOR_UPNP_DELETE);
}
}
else if (m_ExitToDesktopState == ETD_WAITING_FOR_SC_SIGNOUT)
{
if (!m_MyStatus.Pending())
{
SetShutdownState(ETD_WAITING_FOR_UPNP_DELETE);
}
}
else if (m_ExitToDesktopState == ETD_WAITING_FOR_UPNP_DELETE)
{
if (!m_UpNpStatus.Pending())
{
SetShutdownState(ETD_WAITING_FOR_PCP_DELETE);
}
}
else if (m_ExitToDesktopState == ETD_WAITING_FOR_PCP_DELETE)
{
if (!m_PcpStatus.Pending())
{
SetShutdownState(ETD_WAITING_FOR_SCUI_SHUTDOWN);
}
}
else if (m_ExitToDesktopState == ETD_WAITING_FOR_SCUI_SHUTDOWN)
{
if (g_rlPc.GetRgscInterface() == NULL || g_rlPc.GetRgscInterface()->IsReadyToShutdown())
{
SetShutdownState(ETD_READY_TO_SHUTDOWN);
}
}
}
}
bool NetworkExitFlow::FromMP()
{
return m_fromMP;
}
const char* NetworkExitFlow::GetExitString()
{
return (m_fromMP) ? "WARNING_EXIT_SESSION_SHUTDOWN" : "WARNING_EXIT_GAME";
}
#endif
//////////////////////////////////////////////////////////////////////////
// Member vars
//////////////////////////////////////////////////////////////////////////
// EJ: size of heap session for network (combined snet and contextmgr heaps)
#if (RSG_PC || RSG_DURANGO || RSG_ORBIS)
const int CNetwork::SCXNMGR_HEAP_SIZE = (650 * 1024);
#else
const int CNetwork::SCXNMGR_HEAP_SIZE = (128 * 1024);
#endif
static u8 *s_SCxnMgrHeap = 0;
static netMemAllocator *s_SCxnMgrAllocator = 0;
bool CNetwork::sm_bMultiplayerLocked = NETWORK_LOCKED_DEFAULT;
bool CNetwork::sm_bSpPrologueRequired = !RSG_GEN9;
bool CNetwork::sm_bFirstEntryNetworkOpen = false;
bool CNetwork::sm_bNetworkOpen = false;
bool CNetwork::sm_bNetworkOpening = false;
bool CNetwork::sm_bNetworkClosing = false;
bool CNetwork::sm_bNetworkClosePending = false;
bool CNetwork::sm_bNetworkClosePendingIsFull = false;
bool CNetwork::sm_bAllowPendingCloseNetwork = true;
bool CNetwork::sm_bFatalErrorForDoubleOpen = true;
bool CNetwork::sm_bFatalErrorForCloseDuringOpen = true;
bool CNetwork::sm_bFatalErrorForStartWhenClosed = true;
bool CNetwork::sm_bFatalErrorForStartWhenFullyClosed = true;
bool CNetwork::sm_bFatalErrorForStartWhenManagersShutdown = true;
bool CNetwork::sm_bFatalErrorForInvalidPlayerManager = true;
unsigned CNetwork::sm_nMinStallMsgTotalThreshold = 1000;
unsigned CNetwork::sm_nMinStallMsgNetworkThreshold = 100;
bool CNetwork::sm_IsBailPending = false;
bool CNetwork::sm_AlertScriptForBail = false;
bool CNetwork::sm_PendingBailWarningScreen = false;
sBailParameters CNetwork::sm_BailParams;
bool CNetwork::sm_bScriptReadyForEvents = false;
bool CNetwork::sm_bIsScriptAutoMuted = false;
bool CNetwork::sm_bInMPTutorial = false;
bool CNetwork::sm_bInMPCutscene = false;
bool CNetwork::sm_bNetworkCutsceneEntities = false;
bool CNetwork::sm_bFriendlyFireAllowed = false;
bool CNetwork::sm_bManagersAreInitialised = false;
bool CNetwork::sm_bFirstEntryMatchStarted = false;
bool CNetwork::sm_bMatchStarted = false;
bool CNetwork::sm_bGameInProgress = false;
bool CNetwork::sm_bGoStraightToMultiplayer = false;
bool CNetwork::sm_bGoStraightToMpEvent = false;
bool CNetwork::sm_bGoStraightToMPRandomJob = false;
bool CNetwork::sm_bShutdownSessionClearsTheGoStraightToMultiplayerFlag = true;
bool CNetwork::sm_bCollisionAroundLocalPlayer = false;
bool CNetwork::sm_bSkipRadioResetNextClose = false;
bool CNetwork::sm_bSkipRadioResetNextOpen = false;
bool CNetwork::sm_bLiveStreamingInitialised = false;
unsigned CNetwork::sm_bDisableCarGeneratorTime = 0;
u16 CNetwork::sm_nGameMode = MultiplayerGameMode::GameMode_Freeroam;
int CNetwork::sm_nGameModeState = MultiplayerGameModeState::GameModeState_None;
#if __BANK
bool CNetwork::sm_bSaveMigrationEnabled = false;
#endif // __BANK
char CNetwork::sm_logFileBuffer[256];
static netLog *s_MessageLog = nullptr;
static CNetworkBandwidthManager s_BandwidthMgr;
static netTimeSync s_networkClock;
static CNetworkPlayerMgr s_PlayerMgr(s_BandwidthMgr);
static CNetworkVoiceSession s_NetworkVoiceSession;
static CNetworkScratchpads s_ScriptScratchpads;
static CRoamingBubbleMgr s_RoamingBubbleMgr;
static NetQueryFunctions s_QueryFunctions;
static CNetworkLeaderboardMgr s_LeaderboardMgr;
static CNetworkVoice s_NetworkVoice;
#if __WIN32PC
static CNetworkTextChat s_NetworkTextChat;
#endif
static bool s_NetworkCoreInitialised = false;
#if ENABLE_NETWORK_LOGGING
static bool s_AssertionFiredLastFrame = false;
#endif // ENABLE_NETWORK_LOGGING
netTimeSync& CNetwork::sm_networkClock(s_networkClock);
CNetworkObjectMgr* CNetwork::sm_ObjectMgr = 0;
netEventMgr* CNetwork::sm_EventMgr = 0;
CGameArrayMgr* CNetwork::sm_ArrayMgr = 0;
CNetworkBandwidthManager& CNetwork::sm_BandwidthMgr(s_BandwidthMgr);
CNetworkPlayerMgr& CNetwork::sm_PlayerMgr(s_PlayerMgr);
CNetworkSession* CNetwork::sm_NetworkSession = 0;
CNetworkParty* CNetwork::sm_NetworkParty = nullptr;
CNetworkVoiceSession& CNetwork::sm_NetworkVoiceSession(s_NetworkVoiceSession);
CNetworkLeaderboardMgr& CNetwork::sm_LeaderboardMgr(s_LeaderboardMgr);
CNetworkScratchpads& CNetwork::sm_ScriptScratchpads(s_ScriptScratchpads);
CRoamingBubbleMgr& CNetwork::sm_RoamingBubbleMgr(s_RoamingBubbleMgr);
CNetworkVoice& CNetwork::sm_NetworkVoice(s_NetworkVoice);
#if __WIN32PC
CNetworkTextChat& CNetwork::sm_NetworkTextChat(s_NetworkTextChat);
NetworkExitFlow CNetwork::sm_ExitToDesktopFlow;
#endif
netConnectionManager CNetwork::sm_CxnMgr;
netConnectionManager::Delegate CNetwork::sm_CxnMgrGameDlgts[NET_MAX_CHANNELS];
#if !__NO_OUTPUT
netHttpRequestTracker::Delegate CNetwork::sm_RequestTrackerDlgt;
#endif
unsigned CNetwork::sm_syncedTimeInMilliseconds = 0;
unsigned CNetwork::sm_lockedNetworkTime = 0;
unsigned CNetwork::sm_NetworkTimeWhenDebugPaused = 0;
unsigned CNetwork::sm_EventTriggerLastUpdate = 0;
bool sm_WasDebugPausedLastFrame = false;
unsigned s_PausableNetworkTimeOffset = 0;
unsigned s_TimePausableNetworkTimeStopped = 0;
bool s_PausableNetworkTimeStopped = false;
bool CNetwork::sm_IsInTutorialSession = false;
bool s_WasTutorialSessionNetworkTurn = false;
unsigned s_NumPlayersInTutorialSession = 0;
unsigned s_TutorialSessionNetworkTurn = 0;
unsigned s_TutorialSessionTurnDelay = 0;
bool CNetwork::sm_ThereAreConcealedPlayers = false;
bool s_WasConcealedPlayerNetworkTurn = false;
unsigned s_NumPlayersNotConcealedToMe = 0;
unsigned s_ConcealedPlayerNetworkTurn = 0;
unsigned s_ConcealedPlayerTurnDelay = 0;
static CNetworkAssetVerifier s_AssetVerifier;
CNetworkAssetVerifier& CNetwork::sm_AssetVerifier(s_AssetVerifier);
CNetworkTunablesListener* CNetwork::m_pTunablesListener = NULL;
//Calculate average RTT over time for the multiplayer game
static const int DEFAULT_RTT_CHECK_INTERVAL = 1 * 60000; //interval between checking RTT values again
static const unsigned DEFAULT_INITIAL_RTT_CHECK_INTERVAL = 5000; //interval between checking RTT values again
static const unsigned DEFAULT_INITIAL_RTT_SAMPLES = 10; //initial number of samples gathered every DEFAULT_INITIAL_RTT_CHECK_INTERVAL
u32 CNetwork::sm_averageRTT = 0;
u32 CNetwork::sm_averageRTTWorst = 0;
u32 CNetwork::sm_averageRTTSamples = 0;
u32 CNetwork::sm_averageRTTInterval = 0;
int CNetwork::m_nClockHour = 0;
int CNetwork::m_nClockMinute = 0;
int CNetwork::m_nClockSecond = 0;
unsigned CNetwork::m_nClockTransitionTimeLeft = 0;
unsigned CNetwork::m_nClockAndWeatherTransitionTime = 0;
int CNetwork::m_LastClockUpdateTime = 0;
int CNetwork::m_NextClockSyncTime = 0;
bool CNetwork::sm_bAppliedGlobalSettings = false;
bool CNetwork::sm_lastDamageEventPendingOverrideEnabled = true;
// network heap
class sysMemAllocator* CNetwork::sm_pNetworkHeap=NULL;
void* CNetwork::sm_pHeapBuffer=NULL;
int CNetwork::sm_HeapBufferRefCount=0;
#if __BANK
const unsigned MAX_CATEGORY_STACK_LEVELS = 5;
NetworkMemoryCategory s_MemCategoryStack[MAX_CATEGORY_STACK_LEVELS] =
{
NET_MEM_NUM_MANAGERS,
NET_MEM_NUM_MANAGERS,
NET_MEM_NUM_MANAGERS,
NET_MEM_NUM_MANAGERS,
NET_MEM_NUM_MANAGERS
};
unsigned s_CurrentMemCategory = 0;
int s_PreviousHeapUsage = 0;
#endif // __BANK
static bool s_SendTransitionTrackerTelemetry = true;
//Cpu affinity for connection manager.
static const int CXN_MGR_CPU_AFFINITY = 0;
// Maximum size of network log files
static const unsigned MAXIMUM_LOG_FILE_SIZE = 300 * 1024;
///////////////////////////////////////////////////////////////////////////////
// Logging file access interface
///////////////////////////////////////////////////////////////////////////////
class CNetworkLogFileAccess : public netLogFileAccessInterface
{
public:
virtual packedLogDataWriter *CreatePackedLogDataWriter(char *LOGGING_ONLY(logBuffer),
unsigned LOGGING_ONLY(logBufferSize))
{
#if ENABLE_NETWORK_LOGGING
return rage_new packedLogDataWriter(logBuffer, logBufferSize);
#else
return 0;
#endif // ENABLE_NETWORK_LOGGING
}
virtual packedLogDataReader *CreatePackedLogDataReader(const char *LOGGING_ONLY(packedLogData),
unsigned LOGGING_ONLY(packedDataSize),
netLogFileAccessInterface::LogHandle &LOGGING_ONLY(logHandle))
{
#if ENABLE_NETWORK_LOGGING
return rage_new packedLogDataReader(packedLogData, packedDataSize, *this, logHandle);
#else
return 0;
#endif // ENABLE_NETWORK_LOGGING
}
virtual LogHandle OpenFile(const char *filename, LogOpenType openType)
{
FileHandle hFile = INVALID_FILE_HANDLE;
int retryCount = 3;
while (retryCount > 0 && hFile == INVALID_FILE_HANDLE)
{
switch(openType)
{
case LOGOPEN_CREATE:
hFile = CFileMgr::OpenFileForWriting(filename);
break;
case LOGOPEN_APPEND:
{
hFile = CFileMgr::OpenFileForAppending(filename);
if(hFile == INVALID_FILE_HANDLE)
{
hFile = CFileMgr::OpenFileForWriting(filename);
}
}
break;
default:
gnetAssertf(0, "OpenFile :: Invalid Open Type!");
break;
}
// Retry for up to 3 times (3 seconds in total). This is for when the new log grabber locks the log files to copy them
// but they are already overflowing into the next one. This will freeze the game for that period, but we would crash anyway if the logs don't get created.
if (hFile == INVALID_FILE_HANDLE)
{
gnetWarning("CNetworkLogFileAccess::OpenFile Invalid file handle while trying to open file %s (Open Type: %d). Retrying! This will assert if 3 retries fail! Retry count pre-decrement: %d",
filename, openType, retryCount);
sysIpcSleep(1000);
retryCount--;
}
}
gnetAssertf(hFile != INVALID_FILE_HANDLE, "OpenFile :: Invalid handle for %s. Open Type: %d", filename, openType);
return static_cast<LogHandle>(hFile);
}
virtual void CloseFile(LogHandle hLog)
{
FileHandle hFile = static_cast<LogHandle>(hLog);
if(!gnetVerifyf(hFile != INVALID_FILE_HANDLE, "CloseFile :: Invalid handle"))
return;
CFileMgr::CloseFile(hFile);
}
virtual void Flush(LogHandle hLog)
{
FileHandle hFile = static_cast<LogHandle>(hLog);
if(!gnetVerifyf(hFile != INVALID_FILE_HANDLE, "Flush :: Invalid handle"))
return;
CFileMgr::Flush(hFile);
}
virtual void Write(LogHandle hLog, const char *logText, unsigned textSize)
{
FileHandle hFile = static_cast<LogHandle>(hLog);
if(!gnetVerifyf(hFile != INVALID_FILE_HANDLE, "Write :: Invalid handle"))
return;
CFileMgr::Write(hFile, logText, textSize);
}
};
static CNetworkLogFileAccess s_LogFileAccessInterface;
///////////////////////////////////////////////////////////////////////////////
// ScriptValues
///////////////////////////////////////////////////////////////////////////////
void CNetwork::ScriptValues::Clear()
{
sysMemSet(values, 0, sizeof(values));
}
///////////////////////////////////////////////////////////////////////////////
// Clock Override Data
///////////////////////////////////////////////////////////////////////////////
struct ClockOverrideData
{
enum eOverrideType {
OVERRIDE_NONE,
OVERRIDE_TIME,
OVERRIDE_RATE,
};
ClockOverrideData() :
m_Active(OVERRIDE_NONE)
, m_Hour(0)
, m_Min(0)
, m_Second(0)
, m_TimeMinLastAdded(0)
, m_bFromScript(false)
, m_bLocked(false)
{
}
bool IsActive() const { return m_Active != OVERRIDE_NONE; }
bool IsFromScript() const { return m_bFromScript; }
bool IsLocked() const { return m_bLocked; }
void Reset()
{
#if !__NO_OUTPUT
if (m_bLocked)
{
gnetError("Attempting to reset clock override with lock in place");
#if !__FINAL
diagDefaultPrintStackTrace();
#endif
}
#endif
m_Active = OVERRIDE_NONE;
m_Hour = 0;
m_Min = 0;
m_Second = 0;
m_TimeMinLastAdded = 0;
m_bFromScript = false;
m_bLocked = false;
}
void Override(int hour, int min, int second, bool bFromScript)
{
#if !__NO_OUTPUT
if (m_bLocked)
{
gnetError("Attempting to override clock with lock in place");
#if !__FINAL
diagDefaultPrintStackTrace();
#endif
}
#endif
if(!m_Active)
{
m_Active = OVERRIDE_TIME;
m_Hour = CClock::GetHour();
m_Min = CClock::GetMinute();
m_Second = CClock::GetSecond();
m_TimeMinLastAdded = CClock::GetTimeLastMinuteAdded();
}
m_bFromScript = bFromScript;
gnetAssertf(CClock::GetMode()==CLOCK_MODE_DYNAMIC, "calling a dynamic mode function when not in dynamic mode");
CClock::SetTime(hour, min, second);
CClock::SetMsPerGameMinute( CClock::GetDefaultMsPerGameMinute() );
}
void OverrideRate(u32 msPerGameMinute)
{
#if !__NO_OUTPUT
if (m_bLocked)
{
gnetError("Attempting to override clock with lock in place");
#if !__FINAL
diagDefaultPrintStackTrace();
#endif
}
#endif
if (!m_Active)
{
m_Active = OVERRIDE_RATE;
m_Hour = CClock::GetHour();
m_Min = CClock::GetMinute();
m_Second = CClock::GetSecond();
m_TimeMinLastAdded = CClock::GetTimeLastMinuteAdded();
}
m_bFromScript = true; // At the momemnt we dont have any other case, always from script
gnetAssertf(CClock::GetMode() == CLOCK_MODE_DYNAMIC, "calling a dynamic mode function when not in dynamic mode");
CClock::SetMsPerGameMinute(msPerGameMinute);
}
void StopOverriding()
{
#if !__NO_OUTPUT
if (m_bLocked)
{
gnetError("Attempting to remove clock override with lock in place");
#if !__FINAL
diagDefaultPrintStackTrace();
#endif
}
#endif
switch(m_Active)
{
case OVERRIDE_RATE:
CClock::SetMsPerGameMinute( CClock::GetDefaultMsPerGameMinute() );
Reset();
break;
case OVERRIDE_TIME:
CClock::SetTime(m_Hour, m_Min, m_Second);
Reset();
break;
case OVERRIDE_NONE: break;
}
}
void GetStoredSessionTime(int &hour, int &min, int &second, int &timeMinLastAdded)
{
if(gnetVerifyf(IsActive(), "retrieving stored session time when not overriding the game clock!"))
{
hour = m_Hour;
min = m_Min;
second = m_Second;
timeMinLastAdded = m_TimeMinLastAdded;
}
}
void SetStoredSessionTime(int hour, int min, int second, int timeMinLastAdded)
{
if(gnetVerifyf(IsActive(), "updating stored session time when not overriding the game clock!"))
{
m_Hour = hour;
m_Min = min;
m_Second = second;
m_TimeMinLastAdded = timeMinLastAdded;
}
}
bool IsOverrideLocked() const
{
return m_bLocked;
}
void SetOverrideLocked(bool bLocked)
{
m_bLocked = bLocked;
}
eOverrideType m_Active; // whether the override is active
int m_Hour; // stored session game clock time in hours when override activated
int m_Min; // stored session game clock time in minutes when override activated
int m_Second; // stored session game clock time in seconds when override activated
int m_TimeMinLastAdded; // network time when the session game clock last updated the game clock time in minutes stored here
bool m_bFromScript; // whether script applied this override
bool m_bLocked; // whether attempts to update the clock override are locked
};
static ClockOverrideData gClockOverride;
/////////////////////////////////////////////////////////////////////////////////////
// NAME: CNetworkTunablesListener::OnTunablesRead
// PURPOSE: Used to overload network variables from the cloud
/////////////////////////////////////////////////////////////////////////////////////
void CNetworkTunablesListener::OnTunablesRead()
{
gnetDebug1("CNetworkTunablesListener::OnTunablesRead");
// tunables for this file
CNetwork::OnTunablesRead();
// set whether we use time sync token system
s_SendTransitionTrackerTelemetry = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("TELEMETRY_SEND_TRANSITION_TRACKER", 0xedc65aad), s_SendTransitionTrackerTelemetry);
// set whether we use time sync token system
bool bUseTimeSyncToken = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("USE_TIME_SYNC_TOKEN", 0x9bef5adb), bUseTimeSyncToken))
netTimeSync::SetUsingToken(bUseTimeSyncToken);
// blacklisted gamer timeout
int nBlacklistTimeout;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("GAMER_BLACKLIST_TIME", 0xe8f181af), nBlacklistTimeout))
CNetwork::GetNetworkSession().GetBlacklistedGamers().SetBlacklistTimeout(nBlacklistTimeout);
// presence
bool bRejectMismatchedAuthenticatedSender = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NET_PRESENCE_REJECT_MISMATCHED_AUTHENTICATED_SENDER", 0xd3ff022), bRejectMismatchedAuthenticatedSender))
GamePresenceEvents::SetRejectMismatchedAuthenticatedSender(bRejectMismatchedAuthenticatedSender);
// cloud tunables
bool bCloudCacheEncryptedEnabled = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("CLOUD_CACHE_ENCRYPTED_ENABLED", 0x237a6924), bCloudCacheEncryptedEnabled))
CCloudManager::SetCacheEncryptedEnabled(bCloudCacheEncryptedEnabled);
unsigned nullAllocatorPolicy = NullAllocatorPolicy::Policy_UseRline;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("CLOUD_REQUEST_NULL_ALLOCATOR_POLICY", 0xd46f6359), nullAllocatorPolicy))
netCloudRequestGetFile::SetNullAllocatorPolicy(static_cast<NullAllocatorPolicy>(nullAllocatorPolicy));
// rson
bool bAllowRsonSnscanf = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("RSON_ALLOW_SNSCANF", 0xFE3E45D0), bAllowRsonSnscanf))
Rson::SetAllowSnscanf(bAllowRsonSnscanf);
// stall detection
#if !__NO_OUTPUT
s_nLongFrameTime = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NETWORK_LONG_FRAME_TIME", 0xf710bb5c), static_cast<int>(DEFAULT_LONG_FRAME_TIME));
#endif
s_nTimeForLongStallTelemetrySP = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("TIME_FOR_STALL_TELEMETRY_SP", 0xec0004f9), static_cast<int>(DEFAULT_STALL_DETECTION_SP));
s_nTimeForLongStallTelemetryMP = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("TIME_FOR_STALL_TELEMETRY_MP", 0xd1b3eeb9), static_cast<int>(DEFAULT_STALL_DETECTION_MP));
#if RSG_GDK
unsigned inviteResumeGracePeriodMs = 0; // use Access here, we don't know what the default is (behind rlXbl interfaces)
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_INVITE_RESUME_GRACE_PERIOD", 0x57fcdeeb), inviteResumeGracePeriodMs))
rlXbl::SetInviteResumeGracePeriodMs(inviteResumeGracePeriodMs);
#endif
#if RSG_DURANGO
// sessions
unsigned asyncSessionTimeOut = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_ASYNC_TIMEOUT", 0x681aedec), ISessions::DEFAULT_ASYNC_SESSION_TIMEOUT_MS);
unsigned memberReservedTimeout = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_MEMBER_RESERVED_TIMEOUT", 0x12196e09), ISessions::DEFAULT_MEMBER_RESERVED_TIMEOUT_MS);
unsigned memberInactiveTimeout = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_MEMBER_INACTIVE_TIMEOUT", 0x8991c699), ISessions::DEFAULT_MEMBER_INACTIVE_TIMEOUT_MS);
unsigned memberReadyTimeout = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_MEMBER_READY_TIMEOUT", 0xbd1dae07), ISessions::DEFAULT_MEMBER_READY_TIMEOUT_MS);
unsigned sessionEmptyTimeout = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_EMPTY_TIMEOUT", 0xdc1f1113), ISessions::DEFAULT_SESSION_EMPTY_TIMEOUT_MS);
g_rlXbl.GetSessionManager()->SetTimeoutPolicies(asyncSessionTimeOut, memberReservedTimeout, memberInactiveTimeout, memberReadyTimeout, sessionEmptyTimeout);
bool bShouldCancelCommerceTasks = false;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SHOULD_CANCEL_COMMERCE_TASKS", 0x0d891586), bShouldCancelCommerceTasks))
rlXbl::SetShouldCancelCommerceTasks(bShouldCancelCommerceTasks);
bool bRunValidateHost = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_RUN_VALIDATE_HOST", 0x48fe5e12), bRunValidateHost))
g_rlXbl.GetSessionManager()->SetRunValidateHost(bRunValidateHost);
bool bConsiderInvalidHostAsInvalidSession = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_SESSION_CONSIDER_INVALID_HOST_AS_INVALID_SESSION", 0x47ba1d09), bConsiderInvalidHostAsInvalidSession))
g_rlXbl.GetSessionManager()->SetConsiderInvalidHostAsInvalidSession(bConsiderInvalidHostAsInvalidSession);
// parties
bool bLeavePartyOnInvalidGameSession = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_PARTY_LEAVE_ON_INVALID_SESSION", 0x3e636e53), bLeavePartyOnInvalidGameSession))
g_rlXbl.GetPartyManager()->SetLeavePartyOnInvalidGameSession(bLeavePartyOnInvalidGameSession);
// hardware
bool bUseConnectivityLevelForLinkConnected = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("XBL_USE_CONNECTIVITY_FOR_LINK_CONNECTED", 0x70e0a15b), bUseConnectivityLevelForLinkConnected))
netXbl::SetUseConnectivityLevelForLinkConnected(bUseConnectivityLevelForLinkConnected);
// command line overrides
#if !__FINAL
if(PARAM_netXblUseConnectivityForLinkConnected.Get())
netXbl::SetUseConnectivityLevelForLinkConnected(true);
#endif
#endif
#if RL_NP_DEEP_LINK_MANUAL_CLOSE
unsigned browserDeepLinkCloseTime = 0;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NP_BROWSER_DEEP_LINK_CLOSE_TIME", 0x738406c4), browserDeepLinkCloseTime))
rlNpCommonDialog::SetBrowserDeepLinkCloseTime(browserDeepLinkCloseTime);
#endif
#if RSG_SCE
bool dialogFailWhenInitialiseFails = false;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NP_DIALOG_FAIL_WHEN_INITIALISE_FAILS", 0x89502fff), dialogFailWhenInitialiseFails))
rlNpCommonDialog::SetFailWhenInitialiseFails(dialogFailWhenInitialiseFails);
bool bReplayShouldRemapNpOnlineIdsToAccountIds = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("REPLAY_SHOULD_REMAP_NP_ONLINE_IDS_TO_ACCOUNT_IDS", 0x75537c14), bReplayShouldRemapNpOnlineIdsToAccountIds))
ReplayFileManager::SetShouldRemapNpOnlineIdsToAccountIds(bReplayShouldRemapNpOnlineIdsToAccountIds);
#endif
// rlGamerHandle
{
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("RL_GH_ALLOW_EXTRA_VALIDATION_ON_IMPORT", 0xF616982), rlGamerHandle::SetAllowExtraValidationOnImport);
}
// tunneler
{
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_NET_TUNNELER_TELEMETRY", 0x8ABB10FB), netTunneler::SetAllowNetTunnelerTelemetry);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_NET_TUNNELER_ALLOW_KX_RETRY", 0x7B2840E1), netTunneler::SetAllowKeyExchangeRetry);
}
// connection manager
{
// connection manager main thread policy
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("CXN_ACQUIRE_CRIT_SEC_FOR_FULL_MAIN_UPDATE", 0xb4adfead), netConnectionManager::SetAcquireCritSecForFullMainUpdate);
// connection endpoint timeouts
AUTO_TUNABLE_CBS(unsigned, netEndpoint::DEFAULT_INACTIVITY_TIMEOUT_MS, ATSTRINGHASH("ENDPOINT_INACTIVITY_TIMEOUT_MS", 0xc58e20f8), netConnectionManager::SetEndpointInactivityTimeoutMs);
AUTO_TUNABLE_CBS(unsigned, netEndpoint::DEFAULT_CXNLESS_TIMEOUT_MS, ATSTRINGHASH("ENDPOINT_CXNLESS_TIMEOUT_MS", 0x6e546beb), netConnectionManager::SetEndpointCxnlessTimeoutMs);
// threshold for determining when an endpoint's connection quality is poor
AUTO_TUNABLE_CBS(unsigned, netEndpoint::DEFAULT_BAD_QOS_THRESHOLD_MS, ATSTRINGHASH("ENDPOINT_BAD_QOS_THRESHOLD_MS", 0x23F2C485), netConnectionManager::SetBadQosThresholdMs);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("CXN_MGR_ENABLE_OVERALL_QOS", 0x83BC2E8E), netConnectionManager::SetEnableOverallQos);
// connection pending close time
AUTO_TUNABLE_CBS(unsigned, Cxn::DEFAULT_PENDING_CLOSE_MAX_TIME, ATSTRINGHASH("CXN_DEFAULT_PENDING_CLOSE_MAX_TIME", 0x69438f33), Cxn::SetPendingCloseMaxTime);
// connection pending close time
AUTO_TUNABLE_CBS(unsigned, netConnection::DEFAULT_RTT_WARNING_THRESHOLD_MS, ATSTRINGHASH("CXN_RTT_WARNING_THRESHOLD_MS", 0x49BA325F), netConnection::SetRttWarningThresholdMs);
// enable/disable logic that cancels pending tunnel requests that match active endpoints - leaving code in place for now but disabled by default
AUTO_TUNABLE_CBS(bool, false, ATSTRINGHASH("CXN_CANCEL_TUNNELS_MATCHING_ACTIVE_ENDPOINTS", 0xC7186B4A), netConnectionManager::SetCancelTunnelsMatchingActiveEndpoints);
// connection endpoint / connection pools
unsigned nEndpointPoolHint = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ENDPOINT_POOL_HINT", 0xca2cde6d), static_cast<int>(DEFAULT_ENDPOINT_POOL_HINT));
unsigned nCxnPoolHint = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("CXN_POOL_HINT", 0xb2e7a8a5), static_cast<int>(DEFAULT_CXN_POOL_HINT));
CNetwork::GetConnectionManager().SetPoolSizeHints(nEndpointPoolHint, nCxnPoolHint);
}
{
// ICE Tunneler (NAT traversal tunables)
AUTO_TUNABLE_CBS(unsigned, netIceTunneler::DEFAULT_NUM_FAILS_BEFORE_WARNING, ATSTRINGHASH("NUM_NAT_FAILS_BEFORE_WARNING", 0xB08126E), netIceTunneler::SetNumNatFailsBeforeWarning);
AUTO_TUNABLE_CBS(unsigned, netIceTunneler::DEFAULT_NAT_FAIL_RATE_BEFORE_WARNING, ATSTRINGHASH("DEFAULT_NAT_FAIL_RATE_BEFORE_WARNING", 0x45EB1D81), netIceTunneler::SetNatFailRateBeforeWarning);
AUTO_TUNABLE_CBS(unsigned, netIceTunneler::DEFAULT_SYMMETRIC_SOCKET_MODE, ATSTRINGHASH("SYMMETRIC_SOCKET_MODE", 0x4EB315AA), netIceTunneler::SetSymmetricSocketMode);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_SYMMETRIC_INFERENCE", 0x36EF8CCE), netIceTunneler::SetAllowSymmetricInference);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_CANONICAL_CANDIDATE", 0x2B53210E), netIceTunneler::SetAllowCanonicalCandidate);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_SAME_HOST_CANDIDATE", 0xEEB0B72A), netIceTunneler::SetAllowSameHostCandidate);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_CROSS_RELAY_CHECK", 0x8C065D10), netIceTunneler::SetAllowCrossRelayCheck);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_CROSS_RELAY_CHECK_DIRECT_OFFER_TIMED_OUT", 0x24194553), netIceTunneler::SetAllowCrossRelayCheckDirectOfferTimedOut);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_CROSS_RELAY_CHECK_REMOTE_STRICT", 0x8EECB9F0), netIceTunneler::SetAllowCrossRelayCheckRemoteStrict);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_CROSS_RELAY_CHECK_LOCAL_HOST", 0x45850026), netIceTunneler::SetAllowCrossRelayCheckLocalHost);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_ICE_RELAY_ROUTE_CHECK", 0xC2F22061), netIceTunneler::SetAllowRelayRouteCheck);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_PEER_REFLEXIVE_LIST", 0x6A821E8B), netIceTunneler::SetAllowPeerReflexiveList);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_ICE_PRESENCE_QUERY", 0x4B12A1DC), netIceTunneler::SetAllowPresenceQuery);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("PEER_REFLEXIVE_HIGHEST_PLUS_ONE", 0xFEFE87AB), netIceTunneler::SetAllowPeerReflexiveHighestPlusOne);
AUTO_TUNABLE_CBS(unsigned, 15*60, ATSTRINGHASH("PEER_REFLEXIVE_TIMEOUT_SEC", 0x68481C4A), netIceTunneler::SetPeerReflexiveTimeoutSec);
AUTO_TUNABLE_CBS(unsigned, 10, ATSTRINGHASH("MAX_FAILED_SYM_INF_TESTS", 0x650462B8), netIceTunneler::SetMaxFailedSymmetricInferenceTests);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_ICE_TUNNELER_TELEMETRY", 0x29E239A8), netIceTunneler::SetAllowIceTunnelerTelemetry);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_ICE_LOCAL_RELAY_REFRESH", 0x1E3F2A45), netIceTunneler::SetAllowLocalRelayRefresh);
AUTO_TUNABLE_CBS(unsigned, 5000, ATSTRINGHASH("ICE_QUICK_CONNECT_TIMEOUT_MS", 0x6DA758CC), netIceTunneler::SetQuickConnectTimeoutMs);
AUTO_TUNABLE_CBS(unsigned, netIceTunneler::DEFAULT_ROLE_TIE_BREAKER_STRATEGY, ATSTRINGHASH("ICE_ROLE_TIE_BREAKER_STRATEGY", 0x69BBE37B), netIceTunneler::SetRoleTieBreakerStrategy);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_ICE_DIRECT_OFFERS", 0x717C6FD3), netIceTunneler::SetAllowDirectOffers);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_ICE_DIRECT_ANSWERS", 0x1954C5A2), netIceTunneler::SetAllowDirectAnswers);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_RESERVED_CANDIDATES", 0x68D92EE0), netIceTunneler::SetAllowReservedCandidates);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_RESERVED_RFC1918_CANDIDATES", 0xDFE28754), netIceTunneler::SetAllowReservedRfc1918Candidates);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("REQUIRE_RELAY_SUCCESS_FOR_QUICK_CONNECT", 0x2B1A4EBF), netIceTunneler::SetRequireRelaySuccessForQuickConnect);
}
// key exchange
{
AUTO_TUNABLE_CBS(unsigned, 500, ATSTRINGHASH("NET_KX_TRANSACTION_RETRY_INTERVAL_MS", 0x9728CA75), netKeyExchange::SetTransactionRetryIntervalMs);
AUTO_TUNABLE_CBS(unsigned, 7, ATSTRINGHASH("NET_KX_MAX_RETRIES_PER_TRANSACTION", 0x122B2D01), netKeyExchange::SetMaxRetriesPerTransaction);
AUTO_TUNABLE_CBS(unsigned, 5000, ATSTRINGHASH("NET_KX_MAX_SESSION_TIME_MS", 0xBC13DE72), netKeyExchange::SetMaxSessionTimeMs);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("NET_KX_ALLOW_OFFER_RESEND_VIA_RELAY", 0xB21ACAEC), netKeyExchange::SetAllowOfferResendViaRelay);
}
// UPnP / PCP / NAT-PMP
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_PCP_ADDRESS_RESERVATION", 0x477E808F), netNatPcp::SetAllowPcpAddressReservation);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("PCP_RESERVE_RELAY_MAPPED_ADDR", 0xD5E65E9F), netNatPcp::SetReserveRelayMappedAddress);
AUTO_TUNABLE_CBS(unsigned, 20 * 60 * 1000, ATSTRINGHASH("PCP_REFRESH_TIME_MS", 0x96D3972D), netNatPcp::SetPcpRefreshTimeMs);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_UPNP_PORT_FORWARDING", 0x5E735D5), netNatUpNp::SetAllowUpNpPortForwarding);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("RESERVE_RELAY_MAPPED_PORT", 0x62E84D51), netNatUpNp::SetReserveRelayMappedPort);
AUTO_TUNABLE_CBS(unsigned, 20 * 60 * 1000, ATSTRINGHASH("UPNP_REFRESH_TIME_MS", 0xA0AA8D4C), netNatUpNp::SetUpNpPortRefreshTimeMs);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_UPNP_CLEANUP", 0x2F1E5075), netNatUpNp::SetAllowCleanUpOldEntries);
// these systems are disabled until tunables have been received
netNatUpNp::SetTunablesReceived();
netNatPcp::SetTunablesReceived();
// NAT Detector
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_NAT_UDP_TIMEOUT_MEASUREMENT", 0xF735874A), netNatDetector::SetAllowUdpTimeoutMeasurement);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_NAT_ADJUSTABLE_PING_INTERVAL", 0x16CB3D8F), netNatDetector::SetAllowAdjustableUdpSendInterval);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("ALLOW_NAT_INFO_TELEMETRY", 0xFFB91A49), netNatDetector::SetAllowNatInfoTelemetry);
// Relay
AUTO_TUNABLE_CBS(unsigned, 500, ATSTRINGHASH("NET_RELAY_PING_INTERVAL_ADJUSTMENT_MS", 0x5FAAFC4A), netRelay::SetPingIntervalAdjustmentMs);
AUTO_TUNABLE_CBS(unsigned, 0, ATSTRINGHASH("NET_RELAY_PING_INTERVAL_MS", 0x24D9B7E6), netRelay::SetRelayPingIntervalMs);
AUTO_TUNABLE_CBS(unsigned, 500, ATSTRINGHASH("NET_RELAY_PING_RETRY_MS", 0x78387754), netRelay::SetRelayPingRetryMs);
AUTO_TUNABLE_CBS(bool, false, ATSTRINGHASH("NET_RELAY_ALLOW_MP_WITHOUT_RELAY_CXN", 0x74B331C6), netRelay::SetAllowMultiplayerWithoutRelayServer);
AUTO_TUNABLE_CBS(unsigned, 1000 * 60 * 10, ATSTRINGHASH("NET_RELAY_TELEMETRY_SEND_INTERVAL_MS", 0xC84C275F), netRelay::SetRelayTelemetrySendIntervalMs);
// P2P Relay and connection routing
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("NET_ALLOW_CONNECTION_ROUTING", 0x2BB47EFD), netConnectionRouter::SetAllowConnectionRouting);
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("NET_ALLOW_PEER_RELAY", 0x88C0358), netConnectionRouter::SetAllowPeerRelay);
AUTO_TUNABLE_CBS(unsigned, netConnectionRouter::DEFAULT_MAX_HOP_COUNT, ATSTRINGHASH("NET_PEER_RELAY_MAX_HOPS", 0xC9B858CD), netConnectionRouter::SetMaxHopCount);
AUTO_TUNABLE_CBS(unsigned, netConnectionRouter::DEFAULT_MAX_RELAYED_CXNS, ATSTRINGHASH("NET_PEER_RELAY_MAX_RELAYED_CXNS", 0x63693E3A), netConnectionRouter::SetMaxRelayedCxns);
AUTO_TUNABLE_CBS(unsigned, netConnectionRouter::DEFAULT_EMERGENCY_RELAY_FALLBACK_TIME_MS, ATSTRINGHASH("NET_PEER_RELAY_EMERGENCY_RELAY_FALLBACK_TIME_MS", 0xFB9F395D), netConnectionRouter::SetEmergencyRelayFallbackTimeMs);
AUTO_TUNABLE_CBS(unsigned, netConnectionRouter::DEFAULT_EMERGENCY_DIRECT_RECEIVE_TIMEOUT_MS, ATSTRINGHASH("NET_EMERGENCY_DIRECT_RECEIVE_TIMEOUT_MS", 0x4AA1A3A7), netConnectionRouter::SetEmergencyDirectReceiveTimeoutMs);
AUTO_TUNABLE_CBS(unsigned, netConnectionRouter::BROKEN_LINK_DETECTION_TIME_MS, ATSTRINGHASH("NET_PEER_RELAY_BROKEN_LINK_DETECTION_TIME_MS", 0xB59F64F6), netConnectionRouter::SetBrokenLinkDetectionTimeMs);
AUTO_TUNABLE_CBS(unsigned, netConnectionRouter::DEFAULT_ROUTE_CHANGE_WAIT_TIME_MS, ATSTRINGHASH("NET_ROUTE_CHANGE_WAIT_TIME_MS", 0x87DE1ECA), netConnectionRouter::SetRouteChangeWaitTimeMs);
// netSocket
AUTO_TUNABLE_CBS(bool, true, ATSTRINGHASH("SOCKET_SELECT_ON_SEND", 0x4E2FAD23), netSocket::SetRequireSelectOnSend);
// rlProfileStats
AUTO_TUNABLE_CBS(unsigned, rlProfileStats::MAX_NUM_GAMERS_FOR_STATS_READ, ATSTRINGHASH("MAX_NUM_GAMERS_FOR_STATS_READ", 0xd2212993), rlProfileStats::SetMaxNumGamersForStatsRead);
// pc hardware/OS telemetry
bool bAllowPCHardwareTelemetry = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_PC_HARDWARE_TELEMETRY", 0xDDDA97C5), bAllowPCHardwareTelemetry))
CNetworkTelemetry::SetAllowPCHardwareTelemetry(bAllowPCHardwareTelemetry);
// relay usage telemetry
bool bAllowRelayUsageTelemetry = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_RELAY_USAGE_TELEMETRY", 0x1C4562D2), bAllowRelayUsageTelemetry))
CNetworkTelemetry::SetAllowRelayUsageTelemetry(bAllowRelayUsageTelemetry);
// whether to advertise the SESSION_ID matchmaking attribute (required server-side change to go live on prod first)
bool bEnableSessionIdAttribute = false;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("MM_SESSION_ID_ENABLE", 0xde13214b), bEnableSessionIdAttribute))
rlScMatchmaking::EnableSessionIdAttribute(bEnableSessionIdAttribute);
// matchmaking reaper
bool bMatchmakingReaperEnabled = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("MM_REAPER_ENABLE", 0xa8e4ccea), bMatchmakingReaperEnabled))
rlScMatchmaking::EnableMatchReaper(bMatchmakingReaperEnabled);
unsigned nReaperCheckIntervalMs = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("MM_REAPER_CHECK_INTERVAL", 0xb2ccbaae), rlScMatchManager::DEFAULT_CHECK_INTERVAL_MS);
unsigned nReaperRetryIntervalMs = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("MM_REAPER_RETRY_INTERVAL", 0x141a6301), rlScMatchManager::DEFAULT_RETRY_INTERVAL_MS);
rlScMatchmaking::SetMatchReaperIntervals(nReaperCheckIntervalMs, nReaperRetryIntervalMs);
#if NET_ENABLE_MEMBERSHIP_FUNCTIONALITY
// membership
bool bAllowMembershipToExpire = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NET_ALLOW_MEMBERSHIP_TO_EXPIRE", 0x15764830), bAllowMembershipToExpire))
rlScMembership::SetAllowMembershipsToExpire(bAllowMembershipToExpire);
bool bUseMembershipServiceTasks = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NET_USE_MEMBERSHIP_SERVICE_TASKS", 0xcea379e1), bUseMembershipServiceTasks))
rlScMembership::SetUseMembershipServiceTasks(bUseMembershipServiceTasks);
bool bEnableEvaluateBenfits = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NET_ENABLE_EVALUATE_SUBSCRIPTION_BENEFITS", 0x59557150), bEnableEvaluateBenfits))
rlScMembership::SetEnableEvaluateBenefits(bEnableEvaluateBenfits);
#if RL_ALLOW_DUMMY_MEMBERSHIP_DATA
bool bHasMembership = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NET_MEMBERSHIP_DUMMY_HAS_MEMBERSHIP", 0x1de2617f), bHasMembership))
{
int membershipStart = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_MEMBERSHIP_DUMMY_START", 0x2edaa7a3), 0);
int membershipEnd = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_MEMBERSHIP_DUMMY_END", 0x5fccdddd), 0);
rlScMembership::SetDummyMembershipData(bHasMembership, static_cast<u64>(membershipStart), static_cast<u64>(membershipEnd));
}
#endif
#endif
// presence messages
int nPresenceMessageDelayTimeMs = static_cast<int>(rlPresence::DEFAULT_RETRIEVE_MESSAGE_DELAY_MS);
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("PRESENCE_MESSAGE_DELAY_MS", 0xe2902661), nPresenceMessageDelayTimeMs))
rlPresence::SetPresenceMessageDelayTimeMs(static_cast<unsigned>(nPresenceMessageDelayTimeMs));
int nPresenceMessageMaxToRetrieve = static_cast<int>(rlPresence::DEFAULT_MAX_PRESENCE_MESSAGES_TO_RETRIEVE);
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("MAX_PRESENCE_MESSAGES_TO_RETRIEVE", 0xfce61fd8), nPresenceMessageMaxToRetrieve))
rlPresence::SetPresenceMessageMaxToRetrieve(static_cast<unsigned>(nPresenceMessageMaxToRetrieve));
#if SC_PRESENCE
bool bPresenceReadSenderInfo = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("PRESENCE_READ_SENDER_INFO", 0xAA58C713), bPresenceReadSenderInfo))
rlScPresence::SetAllowReadingSenderInfo(bPresenceReadSenderInfo);
#endif
// session
int nMigrateRequestInterval = snMigrateSessionTask::DEFAULT_REQUEST_INTERVAL;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("SESSION_MIGRATE_REQUEST_INTERVAL", 0xa6304cd3), nMigrateRequestInterval))
snMigrateSessionTask::SetMigrateRequestInterval(static_cast<unsigned>(nMigrateRequestInterval));
bool bDataMineEnabled = true;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("SESSION_MM_DATA_MINE_ENABLED", 0x4afb6f45), bDataMineEnabled))
rlSessionManager::SetDataMineEnabled(bDataMineEnabled);
AUTO_TUNABLE_CBS(unsigned, rlSessionManager::DEFAULT_NUM_QUERY_RESULTS_BEFORE_EARLY_OUT, ATSTRINGHASH("NET_NUM_QUERY_RESULTS_BEFORE_EARLY_OUT", 0x6c17bc27), rlSessionManager::SetDefaultNumEarlyOutResults);
AUTO_TUNABLE_CBS(unsigned, rlSessionManager::DEFAULT_MAX_WAIT_SINCE_LAST_RESULT, ATSTRINGHASH("NET_MAX_WAIT_SINCE_LAST_RESULT", 0x246343b7), rlSessionManager::SetDefaultMaxWaitTimeAfterResult);
// cloud cdn
AUTO_TUNABLE_CBS(bool, CUserContentManager::DEFAULT_USE_CDN_TEMPLATE, ATSTRINGHASH("USE_CDN_TEMPLATE", 0x58F899C8), CUserContentManager::SetUseCdnTemplate);
// scauth
bool bScAuthEnabled = rlScAuth::IsEnabled();
if (Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("SCAUTH_ENABLED", 0x805B178E), bScAuthEnabled))
{
rlScAuth::SetEnabled(bScAuthEnabled);
}
else if (PARAM_netNoScAuth.Get())
{
rlScAuth::SetEnabled(false);
}
bool bDisableScAuthExit = rlScAuth::IsExitDisabled();
if (Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("SCAUTH_NOEXIT", 0x6D5268B), bDisableScAuthExit))
{
rlScAuth::DisableExit(bDisableScAuthExit);
}
else if (PARAM_netScAuthNoExit.Get())
{
rlScAuth::DisableExit(true);
}
bool bUseLinkTokens = rlScAuth::DEFAULT_LINK_TOKENS_ENABLED;
if (Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("SCAUTH_USE_LINK_TOKENS", 0xBF991867), bUseLinkTokens))
{
rlScAuth::SetUseLinkTokens(bUseLinkTokens);
}
s_EnableCodeBailAlerting = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_ENABLE_CODE_BAIL_ALERTING", 0xdf7dfb35), false);
// load session tunables
CNetwork::GetNetworkSession().InitTunables();
// load livemanager and sub-system tunables
CLiveManager::OnTunablesRead();
// load text chat tunables
WIN32PC_ONLY(s_NetworkTextChat.OnTunablesRead());
#if __NET_SHOP_ACTIVE
// catalog tunables
bool bNetCatalogCheckForDuplicates = false;
if(Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NET_CATALOG_CHECK_FOR_DUPLICATES", 0x58ea1b2b), bNetCatalogCheckForDuplicates))
netCatalog::SetCheckForDuplicatesOnAddItem(bNetCatalogCheckForDuplicates);
#endif
unsigned nRequestIdThreshold = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("OBJECT_ID_MGR_REQUEST_ID_THRESHOLD", 0x7f357842), netObjectIDMgr::DEFAULT_REQUEST_ID_THRESHOLD);
unsigned nNumIdsToRequest = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("OBJECT_ID_MGR_NUM_IDS_TO_REQUEST", 0x857efcc1), netObjectIDMgr::DEFAULT_NUM_IDS_TO_REQUEST);
unsigned nMinTimeBetweenRequests = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("OBJECT_ID_MGR_MIN_TIME_BETWEEN_REQUESTS", 0xa4cdc466), netObjectIDMgr::DEFAULT_MIN_TIME_BETWEEN_REQUESTS);
unsigned nObjectIdReleaseThreshold = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("OBJECT_ID_MGR_ID_RELEASE_THRESHOLD", 0xfe229f30), netObjectIDMgr::DEFAULT_OBJECT_ID_RELEASE_THRESHOLD);
// command line overrides
#if !__FINAL
PARAM_netObjectIdRequestIdThreshold.Get(nRequestIdThreshold);
PARAM_netObjectIdNumIdsToRequest.Get(nNumIdsToRequest);
PARAM_netObjectIdMinTimeBetweenRequests.Get(nMinTimeBetweenRequests);
PARAM_netObjectIdObjectIdReleaseThreshold.Get(nObjectIdReleaseThreshold);
#endif
netObjectIDMgr::SetRequestIdThreshold(nRequestIdThreshold);
netObjectIDMgr::SetNumIdsToRequest(nNumIdsToRequest);
netObjectIDMgr::SetMinTimeBetweenRequests(nMinTimeBetweenRequests);
netObjectIDMgr::SetObjectIdReleaseThreshold(nObjectIdReleaseThreshold);
// Update vehicle pop tunables
CVehicle::RetrieveAndCacheCloudTunables();
CVehiclePopulation::InitTunables();
CWeaponTarget::InitTunables();
CTaskVehicleCombat::InitTunables();
CTaskUseScenario::InitTunables();
CTaskGetUp::InitTunables();
CTaskExitVehicleSeat::InitTunables();
CTaskMeleeActionResult::InitTunables();
#if CNC_MODE_ENABLED
CTaskIncapacitated::InitTunables();
#endif
CTaskPlayerOnFoot::InitTunables();
CPlayerInfo::InitTunables();
CProjectile::InitTunables();
CVehicleDamage::InitTunables();
CVisualEffects::InitTunables();
#if RL_FACEBOOK_ENABLED
CLiveManager::GetFacebookMgr().InitTunables();
#endif // RL_FACEBOOK_ENABLED
CWeapon::InitTunables();
CStreamVolume::InitTunables();
CIslandHopper::GetInstance().InitTunables();
CPed::InitTunables();
CPlayerSpecialAbilityManager::InitTunables();
camFollowVehicleCamera::InitTunables();
NetworkInterface::GetVoice().InitTunables();
CPedDamageCalculator::InitTunables();
CVehicleGlassComponent::InitTunables();
CBullet::InitTunables();
CExplosionManager::InitTunables();
CVehicleWeaponBattery::InitTunables();
BasePlayerCardDataManager::InitTunables();
UserContentManager::GetInstance().InitTunables();
}
///////////////////////////////////////////////////////////////////////////////
// CNetwork
///////////////////////////////////////////////////////////////////////////////
#if !__NO_OUTPUT
struct LongLoggingTracker
{
struct LongLoggingEntry
{
LongLoggingEntry() { Reset(); }
unsigned m_ExecutionTime;
unsigned m_RelativeTime;
unsigned m_GameFrame;
static const unsigned MAX_CHANNEL_NAME = 32;
char m_ChannelName[MAX_CHANNEL_NAME];
static const unsigned MAX_MESSAGE = 128;
char m_Message[MAX_MESSAGE];
void Reset()
{
m_ExecutionTime = 0;
m_RelativeTime = 0;
m_GameFrame = 0;
m_ChannelName[0] = 0;
m_Message[0] = 0;
}
void Assign(const unsigned executionTime, const unsigned relativeTime, const unsigned gameFrame, const char* channelTag, const char* message)
{
m_ExecutionTime = executionTime;
m_RelativeTime = relativeTime;
m_GameFrame = gameFrame;
safecpy(m_ChannelName, channelTag);
safecpy(m_Message, message);
}
const LongLoggingEntry& operator=(const LongLoggingEntry& other)
{
m_ExecutionTime = other.m_ExecutionTime;
m_RelativeTime = other.m_RelativeTime;
m_GameFrame = other.m_GameFrame;
safecpy(m_ChannelName, other.m_ChannelName);
safecpy(m_Message, other.m_Message);
return *this;
}
};
bool HasFreeEntry() const
{
return m_NumEntries < NUM_LONG_LOGGING_ENTRIES;
}
void Reset()
{
m_NumEntries = 0;
m_LongLoadingOverspillTime = 0;
}
static const unsigned NUM_LONG_LOGGING_ENTRIES = 10;
LongLoggingEntry m_LongLoggingEntries[NUM_LONG_LOGGING_ENTRIES];
unsigned m_NumEntries;
unsigned m_LongLoadingOverspillTime;
};
static LongLoggingTracker s_LongLoggingTrackerBuffer;
static sysCriticalSectionToken s_LongLoggingToken;
void LongLoggingExecutionCallback(const unsigned executionTime, const unsigned relativeTime, const unsigned gameFrame, const char* channelTag, const char* message)
{
SYS_CS_SYNC(s_LongLoggingToken);
if(s_LongLoggingTrackerBuffer.HasFreeEntry())
{
s_LongLoggingTrackerBuffer.m_LongLoggingEntries[s_LongLoggingTrackerBuffer.m_NumEntries].Assign(executionTime, relativeTime, gameFrame, channelTag, message);
s_LongLoggingTrackerBuffer.m_NumEntries++;
return;
}
// if we're still here, we have no free logging entries, just add the execution time to the overspill bucket
s_LongLoggingTrackerBuffer.m_LongLoadingOverspillTime += executionTime;
}
void TakeLongLoggingEntries(LongLoggingTracker& swap)
{
// do this so that we can operate on a snapshot of the buffer without further stalled logging
// including our own, operating on the list
SYS_CS_SYNC(s_LongLoggingToken);
swap = s_LongLoggingTrackerBuffer;
s_LongLoggingTrackerBuffer.Reset();
}
#endif
netSocketManager*
CNetwork::GetSocketMgr()
{
gnetAssert(CLiveManager::IsInitialized());
return CLiveManager::GetSocketMgr();
}
unsigned short
CNetwork::GetSocketPort()
{
static const int DEFAULT_SOCKET_PORT = __FINAL ? 0x4321 : 0x1234;
int port;
#if !__FINAL
if(!PARAM_socketport.Get(port) || !gnetVerifyf(port > 0, "GetSocketPort :: Invalid -socketport command line. Needs to be numeric and above 0"))
{
port = DEFAULT_SOCKET_PORT;
}
#else
port = DEFAULT_SOCKET_PORT;
#endif //__FINAL
gnetAssert(port < 0xFFFF);
if(!gnetVerify(port > 0))
{
//Port will be zero if an invalid value was given on the command line.
port = DEFAULT_SOCKET_PORT;
}
return (unsigned short) port;
}
unsigned
CNetwork::GetVersion()
{
NET_ASSERTS_ONLY(unsigned NUM_VERSION_HIGH_BITS = 16);
const unsigned NUM_VERSION_LOW_BITS = 16;
unsigned versionHi = 0, versionLo = 0;
sscanf(CDebug::GetVersionNumber(), "%d.%d", &versionHi, &versionLo);
gnetAssert(versionHi < (1 << NUM_VERSION_HIGH_BITS));
gnetAssert(versionLo < (1 << NUM_VERSION_LOW_BITS));
const unsigned version = (versionHi << NUM_VERSION_LOW_BITS) | versionLo;
return version;
}
bool
CNetwork::LanSessionsOnly()
{
return PARAM_lan.Get();
}
OUTPUT_ONLY(static void NewLogFileCallback());
void
CNetwork::Init(unsigned initMode)
{
// Register a function to call when diagChannel has opened a new log file.
OUTPUT_ONLY(diagChannel::AddNewFileCallback(&NewLogFileCallback));
gnetDebug1("CNetwork::Init :: InitMode: %d", initMode);
if(initMode == INIT_CORE)
{
//Calculate executable size and cache it.
NetworkInterface::GetExeSize( );
netLog::SetFileAccessInterface(&s_LogFileAccessInterface);
netLog::SetMaximumFileSize(MAXIMUM_LOG_FILE_SIZE);
if (PARAM_nonetwork.Get())
{
InitTunables();
return;
}
#if __BANK
sm_bSaveMigrationEnabled = false;
if (PARAM_enableSaveMigration.Get())
{
sm_bSaveMigrationEnabled = true;
}
#endif // __BANK
sm_syncedTimeInMilliseconds = 0;
sm_lockedNetworkTime = 0;
sm_bGoStraightToMultiplayer=false;
sm_bGoStraightToMpEvent=false;
sm_bGoStraightToMPRandomJob=false;
sm_bShutdownSessionClearsTheGoStraightToMultiplayerFlag = true;
//The connection manager can handle allocation failures.
UseNetworkHeap(NET_MEM_CONNECTION_MGR_HEAP);
s_SCxnMgrHeap = rage_new u8[CNetwork::SCXNMGR_HEAP_SIZE];
// set up our fixed sized buckets. The remaining memory is managed by the simple allocator.
// The fixed allocator heap size is calculated by multiplying the number of chunks in each bucket with the size of chunks in each bucket.
// The bucket sizes are chosen by collecting allocation statistics from QA running with -netMemAllocatorLogging (see netallocator.cpp).
size_t fixedChunkSizes[] = {16, 32, 48, 64, 80, 96, 128, 224, 336, 352, 512, 960};
u8 fixedChunkCounts[COUNTOF(fixedChunkSizes)] = {255, 255, 255, 255, 255, 255, 255, 128, 255, 255, 64, 64}; // about 407k out of our 650k heap
const unsigned numFixedBuckets = COUNTOF(fixedChunkSizes);
CompileTimeAssert(numFixedBuckets <= netMemAllocator::MAX_FIXED_BUCKETS);
s_SCxnMgrAllocator = rage_new netMemAllocator(s_SCxnMgrHeap, CNetwork::SCXNMGR_HEAP_SIZE, sysMemSimpleAllocator::HEAP_NET,
numFixedBuckets, fixedChunkSizes, fixedChunkCounts);
s_SCxnMgrAllocator->SetQuitOnFail(false);
StopUsingNetworkHeap();
// Tell the memory tracker du jour about our network heap.
#if RAGE_TRACKING
//diagTracker* t = diagTracker::GetCurrent();
//if (t && sysMemVisualize::GetInstance().HasNetwork())
//{
// t->InitHeap("Net - SCnxMgr", s_SCxnMgrHeap, CNetwork::SCXNMGR_HEAP_SIZE);
//}
#endif // RAGE_TRACKING
netSocketManager* sktMgr = CNetwork::GetSocketMgr();
sm_CxnMgr.SetMemoryTrackingEnabled(true);
gnetVerify(sm_CxnMgr.Init(s_SCxnMgrAllocator,
DEFAULT_ENDPOINT_POOL_HINT,
DEFAULT_CXN_POOL_HINT,
sktMgr,
CXN_MGR_CPU_AFFINITY,
NULL,
NULL,
NULL));
unsigned timeoutMs = GetTimeoutTime();
ResetConnectionPolicies(timeoutMs);
CNetworkSession::InitRegistrationPolicies();
#if !__FINAL
sm_CxnMgr.SetDisableInactiveEndpointRemoval(timeoutMs == 0);
#endif // !__FINAL
for(int i = 0; i < COUNTOF(sm_CxnMgrGameDlgts); ++i)
{
sm_CxnMgrGameDlgts[i].Bind(&CNetwork::OnNetEvent);
sm_CxnMgr.AddChannelDelegate(&sm_CxnMgrGameDlgts[i], i);
}
rlSessionManager::SetLanMatchingPort(CNetwork::GetSocketPort());
UseNetworkHeap(NET_MEM_LIVE_MANAGER_AND_SESSIONS);
sm_NetworkSession = rage_new CNetworkSession;
sm_NetworkParty = rage_new CNetworkParty;
StopUsingNetworkHeap();
gnetVerify(sm_NetworkSession->Init());
gnetVerify(sm_NetworkParty->Init());
sm_NetworkVoiceSession.Init(CLiveManager::GetRlineAllocator(), &sm_CxnMgr, NETWORK_SESSION_VOICE_CHANNEL_ID);
sm_LeaderboardMgr.Init(CLiveManager::GetRlineAllocator());
sm_NetworkVoice.InitCore(&sm_CxnMgr);
#if RSG_ORBIS
InitialiseLiveStreaming();
#endif
s_NetworkCoreInitialised = true;
datafile_commands::InitialiseThreadPool();
#if !__NO_OUTPUT
// set up http request tracker
netVerifyf(netHttpRequestTracker::Init(), "Error initializing netHttpRequestTracker");
sm_RequestTrackerDlgt.Bind(&CNetwork::OnRequestTrackerEvent);
netHttpRequestTracker::AddDelegate(&sm_RequestTrackerDlgt);
#endif
#if !__FINAL
netFunctionProfiler::Init();
netFunctionProfiler::SetProfilerEnabled(!PARAM_netFunctionProfilerDisabled.Get());
#endif
CNetworkTelemetry::SetupTelemetryStream();
// log out connection manager heap stats after initialization
gnetDebug1("CNetwork::Init :: Connection Manager Heap Stats - Size: %d, Used: %d, Available: %d, Largest Block: %d",
static_cast<int>(s_SCxnMgrAllocator->GetHeapSize()),
static_cast<int>(s_SCxnMgrAllocator->GetMemoryUsed(-1)),
static_cast<int>(s_SCxnMgrAllocator->GetMemoryAvailable()),
static_cast<int>(s_SCxnMgrAllocator->GetLargestAvailableBlock()));
}
else if(initMode == INIT_SESSION)
{
if(PARAM_nonetwork.Get())
return;
USE_MEMBUCKET(MEMBUCKET_NETWORK);
netLog::StartLoggingThread();
const unsigned MESSAGE_LOG_BUFFER_SIZE = 16 * 1024;
static bool s_HasCreatedLog = false;
s_MessageLog = rage_new netLog("Message.log", s_HasCreatedLog ? LOGOPEN_APPEND : LOGOPEN_CREATE, netInterface::GetDefaultLogFileTargetType(),
netInterface::GetDefaultLogFileBlockingMode(), MESSAGE_LOG_BUFFER_SIZE);
s_HasCreatedLog = true;
s_MessageLog->SetUsesStringTable();
#if !__NO_OUTPUT
if(!PARAM_netDisableLoggingStallTracking.Get())
{
rage::diagSetLongExecutionCallback(&LongLoggingExecutionCallback);
}
#endif
NetworkDebug::InitSession();
SetScriptReadyForEvents(false);
sm_bInMPTutorial = false;
sm_bFriendlyFireAllowed = false;
sm_bFirstEntryNetworkOpen = false;
sm_bFirstEntryMatchStarted = false;
CFakePlayerNames::Init();
gClockOverride.Reset();
CNetwork::GetBandwidthManager().BandwidthTest().TestBandwidth(false, nullptr);
ASSERT_ONLY(CQueriableInterface::ValidateTaskInfos());
#if __WIN32PC
sm_ExitToDesktopFlow.ResetExitFlowState();
#endif
}
else if(initMode == INIT_AFTER_MAP_LOADED)
{
if (PARAM_nonetwork.Get())
return;
gnetAssert(CLiveManager::IsInitialized());
sm_bInMPTutorial = false;
sm_bFriendlyFireAllowed = false;
}
CLiveManager::Init(initMode);
if(initMode == INIT_CORE)
{
InitTunables();
m_pTunablesListener = rage_new CNetworkTunablesListener();
#if !__FINAL
// load local tunables after we've registered our network listener
if(gnetVerify(Tunables::IsInstantiated()))
Tunables::GetInstance().LoadLocalFile();
#endif
}
}
void
CNetwork::Shutdown(unsigned shutdownMode)
{
gnetDebug1("CNetwork::Shutdown :: ShutdownMode: %d", shutdownMode);
if(shutdownMode == SHUTDOWN_SESSION)
{
// if we are running any session activities, we need to shutdown
if(NetworkInterface::IsAnySessionActive())
{
// this is done outside of CloseNetwork because there are legitimate cases for that call
// within sessioning (when launching an activity lobby)
sm_NetworkSession->Shutdown(true, eBailReason::BAIL_INVALID);
// immediately initialise again
gnetVerify(sm_NetworkSession->Init());
}
// if the network is open then it needs to be cleaned up and
// pending tasks must be finished.
if(NetworkInterface::IsNetworkOpen())
{
// update once to finish any pending tasks and then close (full close)
CNetwork::Update(true);
CNetwork::CloseNetwork(true);
}
if (sm_bShutdownSessionClearsTheGoStraightToMultiplayerFlag)
{
sm_bGoStraightToMultiplayer = false;
sm_bGoStraightToMpEvent = false;
sm_bGoStraightToMPRandomJob = false;
}
sm_bShutdownSessionClearsTheGoStraightToMultiplayerFlag = true;
NetworkDebug::ShutdownSession();
delete s_MessageLog;
s_MessageLog = nullptr;
netLog::StopLoggingThread();
WIN32PC_ONLY(sm_ExitToDesktopFlow.ResetExitFlowState());
}
else if(shutdownMode == SHUTDOWN_CORE)
{
datafile_commands::ShutdownThreadPool();
if (PARAM_nonetwork.Get())
{
ShutdownTunables();
return;
}
delete m_pTunablesListener;
m_pTunablesListener = NULL;
for(int i = 0; i < COUNTOF(sm_CxnMgrGameDlgts); ++i)
{
sm_CxnMgr.RemoveChannelDelegate(&sm_CxnMgrGameDlgts[i]);
}
#if !__NO_OUTPUT
netHttpRequestTracker::RemoveDelegate(&sm_RequestTrackerDlgt);
#endif
UseNetworkHeap(NET_MEM_LEADERBOARD_DATA);
sm_LeaderboardMgr.Shutdown();
StopUsingNetworkHeap();
sm_NetworkSession->Shutdown(false, eBailReason::BAIL_INVALID);
sm_NetworkParty->Shutdown();
UseNetworkHeap(NET_MEM_LIVE_MANAGER_AND_SESSIONS);
delete sm_NetworkSession;
delete sm_NetworkParty;
StopUsingNetworkHeap();
sm_NetworkSession = nullptr;
sm_NetworkParty = nullptr;
sm_NetworkVoiceSession.Shutdown(false);
WIN32PC_ONLY(sm_NetworkTextChat.Shutdown());
// close the Network
CNetwork::CloseNetwork(true);
// shutdown voice
ShutdownVoice(true);
sm_NetworkVoice.ShutdownCore();
sm_CxnMgr.Shutdown();
UseNetworkHeap(NET_MEM_CONNECTION_MGR_HEAP);
delete [] s_SCxnMgrHeap;
delete s_SCxnMgrAllocator;
s_SCxnMgrHeap = 0;
s_SCxnMgrAllocator = 0;
StopUsingNetworkHeap();
// shutdown live streaming
#if RSG_ORBIS
ShutdownLiveStreaming();
#endif
NOTFINAL_ONLY(netFunctionProfiler::Shutdown());
s_NetworkCoreInitialised = false;
}
CLiveManager::Shutdown(shutdownMode);
if(shutdownMode == SHUTDOWN_CORE)
{
ShutdownTunables();
}
}
void
CNetwork::InitTunables()
{
Tunables::Instantiate();
Tunables::GetInstance().Init();
}
void
CNetwork::ShutdownTunables()
{
if(Tunables::IsInstantiated())
{
Tunables::GetInstance().Shutdown();
}
}
void
CNetwork::ShutdownConnectionManager()
{
sm_CxnMgr.Shutdown();
}
void
CNetwork::OnTunablesRead()
{
// check for connection policy changes (timeout or keep alive interval)
bool bTunableChanges = false;
bTunableChanges |= Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NETWORK_TIME_OUT", 0x25f8696e), CNetwork::DEFAULT_CONNECTION_TIMEOUT);
bTunableChanges |= Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("NETWORK_KEEP_ALIVE_INTERVAL", 0xfaf51383), CNetwork::DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL);
if(bTunableChanges)
ResetConnectionPolicies(CNetwork::GetTimeoutTime(), static_cast<unsigned>(CNetwork::DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL));
// tunables for open / close invalid states
sm_bAllowPendingCloseNetwork = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_PENDING_CLOSE_NETWORK", 0x8a98d7d0), true);
sm_bFatalErrorForDoubleOpen = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("FATAL_ERROR_FOR_DOUBLE_OPEN", 0xbd08c6f7), true);
sm_bFatalErrorForCloseDuringOpen = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("FATAL_ERROR_FOR_CLOSE_DURING_OPEN", 0xb495efa5), true);
sm_bFatalErrorForStartWhenClosed = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("FATAL_ERROR_FOR_START_WHEN_CLOSED", 0x7eb2601c), true);
sm_bFatalErrorForStartWhenFullyClosed = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("FATAL_ERROR_FOR_START_WHEN_FULLY_CLOSED", 0x920124a3), true);
sm_bFatalErrorForStartWhenManagersShutdown = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("FATAL_ERROR_FOR_START_WHEN_MANAGERS_SHUTDOWN", 0xb2bb6ec4), true);
sm_bFatalErrorForInvalidPlayerManager = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("FATAL_ERROR_FOR_INVALID_PLAYER_MGR", 0x294b3787), true);
// tunables for sending stall messages to other players
sm_nMinStallMsgTotalThreshold = Tunables::GetInstance().TryAccess(MP_GLOBAL_HASH, ATSTRINGHASH("MIN_STALL_MSG_TOTAL_THRESHOLD", 0x33d23b75), 1000);
sm_nMinStallMsgNetworkThreshold = Tunables::GetInstance().TryAccess(MP_GLOBAL_HASH, ATSTRINGHASH("MIN_STALL_MSG_NETWORK_THRESHOLD", 0xb1398c19), 100);
CLiveManager::GetCrewDataMgr().OnTunableRead();
}
OUTPUT_ONLY(static void WriteSummaryIfLogFileIsNew());
void
CNetwork::Update(bool fromLoadingCode)
{
if(!s_NetworkCoreInitialised && !PARAM_nonetwork.Get())
{
return;
}
#if !__FINAL
scrThread::CheckIfMonitoredScriptArraySizeHasChanged("CNetwork::Update() - start", 0);
#endif
OUTPUT_ONLY(WriteSummaryIfLogFileIsNew());
// don't process again if we are already in the network update function
// (for recursion)
static int inNetworkUpdate = 0;
// stall detection
static unsigned s_LastTime = sysTimer::GetSystemMsTime();
const unsigned nCurrentTime = sysTimer::GetSystemMsTime();
#if !__FINAL && !__PROFILE
// disable any functions that we call within loading code from also pushing / popping
netFunctionProfiler::SetEnablePushAndPop(!fromLoadingCode);
if(0 == inNetworkUpdate)
{
netFunctionProfiler::GetProfiler().SetShouldProfile(!fromLoadingCode);
if(!fromLoadingCode) // clean the profiler if we finished the network update function
{
netFunctionProfiler::GetProfiler().StartFrame();
}
}
if(!fromLoadingCode) // clean the profiler if we finished the network update function
{
netFunctionProfiler::GetProfiler().PushProfiler("CNetwork::Update", PARAM_netFunctionProfilerAsserts.Get());
unsigned profilerTreshold = 1;
PARAM_netFunctionProfilerThreshold.Get(profilerTreshold);
netFunctionProfiler::GetProfiler().SetTimeThreshold(profilerTreshold);
}
#endif // !__FINAL && !__PROFILE
UpdateSyncedTimeInMilliseconds();
if (!PARAM_nonetwork.Get())
{
USE_MEMBUCKET(MEMBUCKET_NETWORK);
if (fwTimer::IsUserPaused() && sm_networkClock.HasSynched())
{
if(!sm_WasDebugPausedLastFrame)
{
sm_NetworkTimeWhenDebugPaused = sm_networkClock.GetTime();
gnetDebug1("Network Timer Paused at %d", sm_NetworkTimeWhenDebugPaused);
}
sm_WasDebugPausedLastFrame = true;
}
else
{
if(sm_WasDebugPausedLastFrame)
gnetDebug1("Network Timer Unpaused at %d", sm_NetworkTimeWhenDebugPaused);
sm_WasDebugPausedLastFrame = false;
}
#if !__FINAL
UPDATE_PROFILE(CNetworkSoakTests::Update())
#endif // !__FINAL
}
// don't do these during a keep alive
if(!fromLoadingCode)
{
// need to bail if we tried during an open
if(sm_IsBailPending && !sm_bNetworkOpening)
{
gnetDebug1("CNetwork::Update :: Actioning pending bail");
ActionBail();
sm_IsBailPending = false;
}
// need to close if we tried during an open
if(sm_bNetworkClosePending && !sm_bNetworkOpening)
{
gnetDebug1("CNetwork::Update :: Actioning pending close");
CloseNetwork(sm_bNetworkClosePendingIsFull);
sm_bNetworkClosePending = false;
}
}
UpdateNetworkAlertScreen();
// we don't update the timers when this function is called from loading code.
// This is because the function can be called many times per frame which can
// overflow the time bars.
netInterface::SetShouldUpdateTimebars(!fromLoadingCode);
netTimeBarWrapper timeBarWrapper(netInterface::ShouldUpdateTimebars());
++inNetworkUpdate;
const unsigned curTime = sysTimer::GetSystemMsTime();
if(1 == inNetworkUpdate)
{
bool bUseTimebars = sm_NetworkSession && sm_NetworkSession->IsSessionActive();
if (bUseTimebars)
timeBarWrapper.PushBudgetTimer("Network", 3.0f);
else
timeBarWrapper.PushTimer("Network",false);
if (!PARAM_nonetwork.Get())
{
//Always update the connection manager and the session in order
//to handle events that occur while we're restarting and
//we have an active rendezvous session.
// Call the connection manager's Update() every frame
// to read/write messages to/from the network socket
if(bUseTimebars) timeBarWrapper.StartTimer("netConnectionManager", false);
//s_CxnMgrAllocator.SanityCheck();
UPDATE_PROFILE(sm_CxnMgr.Update(curTime))
#if !__FINAL
scrThread::CheckIfMonitoredScriptArraySizeHasChanged("sm_CxnMgr.Update()", 0);
#endif
if(bUseTimebars) timeBarWrapper.StartTimer("NetworkSession", false);
if(sm_NetworkSession)
{
UPDATE_PROFILE(sm_NetworkSession->Update())
}
if (sm_NetworkParty)
{
UPDATE_PROFILE(sm_NetworkParty->Update())
}
UPDATE_PROFILE(sm_NetworkVoiceSession.Update())
if(bUseTimebars) timeBarWrapper.StartTimer("LeaderboardManager");
UPDATE_PROFILE(sm_LeaderboardMgr.Update())
// manage clock and weather update
UPDATE_PROFILE(UpdateClockAndWeather())
// manage additional SCUI hotkeys (pc-only)
UPDATE_PROFILE(UpdateScuiHotkeys())
if(!fromLoadingCode)
{
const unsigned nCurrentTimeIncNetworkUpdate = sysTimer::GetSystemMsTime();
static unsigned s_LastTimeIncNetworkUpdate = sysTimer::GetSystemMsTime();
static unsigned s_LastPosix = static_cast<unsigned>(rlGetPosixTime());
static bool s_bWasInMultiplayer = NetworkInterface::IsAnySessionActive();
const bool bIsInMultiplayer = NetworkInterface::IsAnySessionActive();
static bool s_bWasInTransition = NetworkInterface::IsTransitioning();
static bool s_bWasLaunching = NetworkInterface::IsLaunchingTransition();
const unsigned nElapsedTimeIncNetworkUpdate = (nCurrentTimeIncNetworkUpdate - s_LastTimeIncNetworkUpdate);
const unsigned nElapsedTime = (nCurrentTime - s_LastTime);
// log when the time between updates is more than s_nLongFrameTime ms.
// this will also fire for 'known' stalls such breakpoints, adding a bug
// or warping the player but it's only a log entry
if(bIsInMultiplayer && s_bWasInMultiplayer && (nElapsedTimeIncNetworkUpdate > s_nLongFrameTime))
{
// if the elapsed time is greater than 1 second, pull an updated posix from the server to 'verify' the stall
#if !__NO_OUTPUT
const unsigned nCurrentPosix = static_cast<unsigned>(rlGetPosixTime(nElapsedTimeIncNetworkUpdate > 1000));
gnetWarning("NetStallDetect :: Network :: Stall of %ums (Network Update: %ums, Current: %dms, Last: %dms, Posix: %u, LastPosix: %u, ElapsedPosix: %u", nElapsedTimeIncNetworkUpdate, nElapsedTimeIncNetworkUpdate - nElapsedTime, nCurrentTime, s_LastTime, nCurrentPosix, s_LastPosix, nCurrentPosix - s_LastPosix);
#endif
// if it exceeds one of two thresholds, send a warning about the stall to connected players
if ((nElapsedTimeIncNetworkUpdate > sm_nMinStallMsgTotalThreshold) ||
(nElapsedTimeIncNetworkUpdate - nElapsedTime) > sm_nMinStallMsgNetworkThreshold)
{
sm_NetworkSession->SendStallMessage(nElapsedTimeIncNetworkUpdate, nElapsedTimeIncNetworkUpdate - nElapsedTime);
}
}
// this is when script have kicked off, guard using this to avoid any stalls during boot
if(IsScriptReadyForEvents() NOTFINAL_ONLY(&& PARAM_netSendStallTelemetry.Get()))
{
if(bIsInMultiplayer && s_bWasInMultiplayer && (nElapsedTimeIncNetworkUpdate > s_nTimeForLongStallTelemetryMP))
{
u64 nSessionToken = 0, nSessionID = 0;
if(sm_NetworkSession)
{
const snSession& rSession = sm_NetworkSession->GetSnSession();
nSessionToken = rSession.Exists() ? rSession.GetSessionToken().GetValue() : 0;
nSessionID = rSession.GetSessionId();
}
gnetDebug1("NetStallDetect :: StallTelemetry :: Stall: %ums, Network: %ums, Transition: %s, Launching: %s, Host: %s, TimeoutsPending: %u, Size: %u, Token: 0x%016" I64FMT "x, Id: 0x%016" I64FMT "x",
nElapsedTimeIncNetworkUpdate,
nElapsedTimeIncNetworkUpdate - nElapsedTime,
s_bWasInTransition ? "True" : "False",
s_bWasLaunching ? "True" : "False",
NetworkInterface::IsHost() ? "True" : "False",
sm_CxnMgr.GetNumTimeoutPending(),
sm_PlayerMgr.IsInitialized() ? sm_PlayerMgr.GetNumActivePlayers() : 0,
nSessionToken,
nSessionID);
CNetworkTelemetry::StallDetectedMultiPlayer(nElapsedTimeIncNetworkUpdate,
nElapsedTimeIncNetworkUpdate - nElapsedTime,
s_bWasInTransition,
s_bWasLaunching,
NetworkInterface::IsHost(),
sm_CxnMgr.GetNumTimeoutPending(),
sm_PlayerMgr.IsInitialized() ? sm_PlayerMgr.GetNumActivePlayers() : 0,
nSessionToken,
nSessionID);
// let session know
sm_NetworkSession->OnStallDetected(nElapsedTimeIncNetworkUpdate);
}
else if(!bIsInMultiplayer && !s_bWasInMultiplayer && (nElapsedTimeIncNetworkUpdate > s_nTimeForLongStallTelemetrySP))
CNetworkTelemetry::StallDetectedSinglePlayer(nElapsedTimeIncNetworkUpdate, nElapsedTimeIncNetworkUpdate - nElapsedTime);
}
s_LastPosix = static_cast<unsigned>(rlGetPosixTime());
s_LastTimeIncNetworkUpdate = sysTimer::GetSystemMsTime();
s_bWasInMultiplayer = bIsInMultiplayer;
s_bWasInTransition = NetworkInterface::IsTransitioning();
s_bWasLaunching = NetworkInterface::IsLaunchingTransition();
#if !__NO_OUTPUT
// long logging tracker
if(s_LongLoggingTrackerBuffer.m_NumEntries > 0)
{
LongLoggingTracker tracker;
TakeLongLoggingEntries(tracker);
for(unsigned i = 0; i < tracker.m_NumEntries; i++)
{
gnetWarning("NetStallDetect :: Long Logging Execution: %ums, Msg: [%08u:%08u] %s: %s",
tracker.m_LongLoggingEntries[i].m_ExecutionTime,
tracker.m_LongLoggingEntries[i].m_RelativeTime,
tracker.m_LongLoggingEntries[i].m_GameFrame,
tracker.m_LongLoggingEntries[i].m_ChannelName,
tracker.m_LongLoggingEntries[i].m_Message);
}
if(tracker.m_LongLoadingOverspillTime)
{
gnetWarning("NetStallDetect :: Long Logging Overspill: %ums", tracker.m_LongLoadingOverspillTime);
}
}
#endif
}
if(sm_bNetworkOpen)
{
if (sm_networkClock.HasStarted())
{
timeBarWrapper.StartTimer("Time Management");
UPDATE_PROFILE(sm_networkClock.Update())
}
else
{
s_PausableNetworkTimeStopped = false;
s_PausableNetworkTimeOffset = 0;
s_TimePausableNetworkTimeStopped = 0;
}
// update locked time
sm_lockedNetworkTime = GetNetworkTime();
// update pausable network time
if(s_PausableNetworkTimeStopped)
{
s_PausableNetworkTimeOffset = sm_lockedNetworkTime - s_TimePausableNetworkTimeStopped;
}
if (sm_bManagersAreInitialised)
{
#if !__FINAL
scrThread::CheckIfMonitoredScriptArraySizeHasChanged("CNetwork::Update", 0);
#endif
timeBarWrapper.StartTimer("Misc Networking Systems", false);
timeBarWrapper.StartTimer("Player Manager");
UPDATE_PROFILE(sm_PlayerMgr.Update())
timeBarWrapper.PushTimer("NetworkDebug");
UPDATE_PROFILE(NetworkDebug::Update())
timeBarWrapper.PopTimer();
// update the network components of the script handlers
timeBarWrapper.StartTimer("Script handlers");
UPDATE_PROFILE(CTheScripts::GetScriptHandlerMgr().NetworkUpdate())
#if !__FINAL
scrThread::CheckIfMonitoredScriptArraySizeHasChanged("CTheScripts::GetScriptHandlerMgr().NetworkUpdate()", 0);
#endif
// The rest of the managers are only updated once we have hosted or joined a session.
// We need to wait until the network clock is synchronized properly.
if (NetworkInterface::IsInSession())
{
timeBarWrapper.StartTimer("Bubble Manager");
UPDATE_PROFILE(sm_RoamingBubbleMgr.Update())
timeBarWrapper.StartTimer("Bandwidth Manager");
UPDATE_PROFILE(sm_BandwidthMgr.Update())
// don't update the rest of the managers until our local player has been assigned a bubble and a physical player index.
if (NetworkInterface::GetLocalPlayer()->IsInRoamingBubble())
{
timeBarWrapper.PushTimer("Array Manager", false);
UPDATE_PROFILE(sm_ArrayMgr->Update())
timeBarWrapper.PopTimer(false);
#if !__FINAL
scrThread::CheckIfMonitoredScriptArraySizeHasChanged("sm_ArrayMgr->Update()", 0);
#endif
// the event mgr update must come before the object mgr update, because otherwise we can get the
// situation where a clone is updated, then migrates due to an incoming GiveControl event, then it
// is updated again as a local object in CWorldProcess. So in effect ProcessControl is called twice
// resulting in some problems (eg. assert(0) in PedMoveBlendOnFoot)
timeBarWrapper.StartTimer("Event Manager", false);
UPDATE_PROFILE(sm_EventMgr->Update());
UpdateEventTriggerCounters();
// update the cached flag for whether there is collision around the local player (using when updating network objects)
if(FindPlayerPed() && FindPlayerPed()->GetNetworkObject())
{
sm_bCollisionAroundLocalPlayer = g_StaticBoundsStore.GetBoxStreamer().HasLoadedAboutPos(FindPlayerPed()->GetTransform().GetPosition(), fwBoxStreamerAsset::FLAG_STATICBOUNDS_MOVER);
}
timeBarWrapper.PushTimer("Object Manager", false);
UPDATE_PROFILE(sm_ObjectMgr->Update(!fromLoadingCode))
timeBarWrapper.PopTimer(false);
UPDATE_PROFILE(UpdateConcealedPlayerCachedVariables())
UPDATE_PROFILE(UpdateTutorialSessionCachedVariables())
UPDATE_PROFILE(NetworkHasherUtil::UpdateResultsQueue(fromLoadingCode))
#if !__FINAL
scrThread::CheckIfMonitoredScriptArraySizeHasChanged("sm_ArrayMgr->Update()", 0);
#endif
}
}
UPDATE_PROFILE(CNetworkStreamingRequestManager::Update())
// scope block for profile block
{
UPDATE_PROFILE_BLOCK(netLoggingInterface::PostNetworkUpdate);
sm_ArrayMgr->GetLog().PostNetworkUpdate();
sm_EventMgr->GetLog().PostNetworkUpdate();
sm_ObjectMgr->GetLog().PostNetworkUpdate();
sm_ObjectMgr->GetReassignMgr().GetLog().PostNetworkUpdate();
sm_PlayerMgr.GetLog().PostNetworkUpdate();
sm_BandwidthMgr.GetLog().PostNetworkUpdate();
if(s_MessageLog != nullptr)
{
s_MessageLog->PostNetworkUpdate();
}
}
UPDATE_PROFILE(CNetObjPlayer::UpdateHighestWantedLevelInArea())
}
if(CTheScripts::GetScriptHandlerMgr().GetLog())
{
CTheScripts::GetScriptHandlerMgr().GetLog()->PostNetworkUpdate();
}
//Calculate average RTT over time for the multiplayer game.
// - This only works when you are the client since it is using the network clock RTT.
if (sm_averageRTTInterval < curTime
|| (sm_averageRTTSamples < DEFAULT_INITIAL_RTT_SAMPLES && sm_averageRTTInterval+DEFAULT_INITIAL_RTT_CHECK_INTERVAL < curTime))
{
int interval = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("RTT_CHECK_INTERVAL", 0x0293014a), DEFAULT_RTT_CHECK_INTERVAL);
sm_averageRTTInterval = curTime+interval;
sm_averageRTT += sm_networkClock.GetAverageRTT();
if (sm_networkClock.GetAverageRTT() > sm_averageRTTWorst)
{
sm_averageRTTWorst = sm_networkClock.GetAverageRTT();
}
++sm_averageRTTSamples;
static StatId avgRTT = ATSTRINGHASH("MPPLY_AVERAGE_RTT", 0x5047E8CC);
StatsInterface::SetStatData(avgRTT, (int)GetClientAverageRTT());
}
if(sm_bDisableCarGeneratorTime > 0)
{
sm_bDisableCarGeneratorTime -= rage::Min(sm_bDisableCarGeneratorTime, fwTimer::GetSystemTimeStepInMilliseconds());
}
}
#if __WIN32PC
sm_ExitToDesktopFlow.Update();
#endif // __WIN32PC
if(bUseTimebars) timeBarWrapper.StartTimer("Voice", false);
UPDATE_PROFILE(sm_NetworkVoice.Update())
#if __WIN32PC
if(sm_NetworkTextChat.IsInitialized())
{
sm_NetworkTextChat.Update();
}
#endif
UPDATE_PROFILE(netRemoteLog::UpdateClass())
// update tunables
if(bUseTimebars) timeBarWrapper.StartTimer("Tunables", false);
UPDATE_PROFILE(Tunables::GetInstance().Update())
} // end !PARAM_nonetwork.Get
if(bUseTimebars) timeBarWrapper.StartTimer("LiveManager", false);
UPDATE_PROFILE(CLiveManager::Update(curTime | 0x01))
timeBarWrapper.PopTimer(false); //Network
//CLiveManager calls rlUpdate(), which updates ongoing NAT
//negotiations. Be sure to continue updating while loading.
}
--inNetworkUpdate;
gnetAssert(inNetworkUpdate >= 0);
#if ENABLE_NETWORK_LOGGING
if(s_AssertionFiredLastFrame)
{
UPDATE_PROFILE(CNetwork::FlushAllLogFiles(true))
s_AssertionFiredLastFrame = false;
}
#endif // ENABLE_NETWORK_LOGGING
#if __ASSERT
fwTimer::SetIsNetworkGame(IsGameInProgress());
#endif
#if !__FINAL && !__PROFILE
if(!fromLoadingCode) // clean the profiler if we finished the network update function
{
netFunctionProfiler::GetProfiler().PopProfiler("CNetwork::Update", s_nLongFrameTime);
if(0 == inNetworkUpdate) // clean the profiler if we finished the network update function
{
netFunctionProfiler::GetProfiler().TerminateFrame();
}
}
// always enable this at the end of the frame
netFunctionProfiler::SetEnablePushAndPop(true);
#endif
#if !__NO_OUTPUT
// http request tracking
netHttpRequestTracker::Update();
#endif
// do this at the bottom so that we don't include a long network frame in our detection
if(0 == inNetworkUpdate && !fromLoadingCode)
{
s_LastTime = sysTimer::GetSystemMsTime();
#if !__NO_OUTPUT
// log when the time for CNetwork::Update is more than s_nLongFrameTime ms.
const unsigned nElapsedUpdateTime = (s_LastTime - nCurrentTime);
if(nElapsedUpdateTime > s_nLongFrameTime)
{
gnetWarning("NetStallDetect :: Network :: Long Update of %dms (Top: %dms, Bottom: %dms) detected at %" I64FMT "u", nElapsedUpdateTime, nCurrentTime, s_LastTime, rlGetPosixTime());
}
#endif //!__NO_OUTPUT
}
CNetworkEventsDumpInfo::UpdateNetworkEventsCrashDumpData();
}
u32
CNetwork::GetClientAverageRTT()
{
u32 averagertt = sm_averageRTT;
if (sm_averageRTTSamples == 2)
{
averagertt /= 2;
}
else if (sm_averageRTTSamples>2)
{
averagertt = (sm_averageRTT-sm_averageRTTWorst) / (sm_averageRTTSamples-1);
}
return averagertt;
}
bool IsUsingDebugClock()
{
bool bUsingDebugClock = false;
#if !__FINAL
bUsingDebugClock |= CClock::HasDebugModifiedInNetworkGame();
#endif
#if __BANK
bUsingDebugClock |= CClock::GetTimeOverrideFlagRef();
#endif
return bUsingDebugClock;
}
void CNetwork::ApplyClockAndWeather(bool bDoTransition, unsigned nTransitionTime)
{
// no transition, flatten the timer
if(!bDoTransition)
m_nClockAndWeatherTransitionTime = 0;
// do transition but not time given
else if(bDoTransition)
{
if(nTransitionTime == 0)
{
int sTransitionTime = 0;
bool bFound = Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("CLOCK_AND_WEATHER_TRANSITION_TIME", 0x8cb9ff24), sTransitionTime);
if(bFound)
m_nClockAndWeatherTransitionTime = static_cast<unsigned>(sTransitionTime);
else
{
static const unsigned DEFAULT_TRANSITION_TIME = 5000;
m_nClockAndWeatherTransitionTime = DEFAULT_TRANSITION_TIME;
}
}
else
m_nClockAndWeatherTransitionTime = nTransitionTime;
}
// grab the global date
int nGameDay;
GetGlobalDate(nGameDay);
static const s32 MULTIPLAYER_YEAR_START = 2013;
CClock::SetDate(1, Date::JANUARY, MULTIPLAYER_YEAR_START);
CClock::AddDays(nGameDay);
// date logging
netTodDebug("ApplyClockAndWeather :: Date is %02d,%02d,%04d. DoW: %d. Ratio: %.2f", CClock::GetDay(), CClock::GetMonth(), CClock::GetYear(), CClock::GetDayOfWeek(), GetGlobalDateRatio());
// grab the global clock time
int nHour, nMinute, nSecond;
GetGlobalClock(nHour, nMinute, nSecond);
// if the clock time is overridden, we need to update the stored values
if(CNetwork::IsClockTimeOverridden())
CNetwork::HandleClockUpdateDuringOverride(nHour, nMinute, nSecond, sysTimer::GetSystemMsTime());
#if !__FINAL
else
{
if(!IsUsingDebugClock())
{
// reset transition timer
m_nClockTransitionTimeLeft = m_nClockAndWeatherTransitionTime;
// start overridding with current time if we're kicking off a transition
if(m_nClockTransitionTimeLeft > 0)
{
m_nClockHour = CClock::GetHour();
m_nClockMinute = CClock::GetMinute();
m_nClockSecond = CClock::GetSecond();
CNetwork::OverrideClockTime(m_nClockHour, m_nClockMinute, m_nClockSecond, false);
}
// apply and override the clock time
CClock::SetTime(nHour, nMinute, nSecond);
}
}
#endif
// clock logging
netTodDebug("ApplyClockAndWeather :: Current global time is %02d:%02d:%02d. Ratio: %.2f", nHour, nMinute, nSecond, GetGlobalClockRatio());
// grab the global weather time
s32 nCycleIndex;
u32 nCycleTimer;
GetGlobalWeather(nCycleIndex, nCycleTimer);
if(!g_weather.GetNetIsOverriding())
{
// apply the global weather time
g_weather.NetworkInitFromGlobal(nCycleIndex, nCycleTimer, m_nClockAndWeatherTransitionTime);
}
// weather logging
NOTFINAL_ONLY(netTodDebug("ApplyClockAndWeather :: Current global weather is Index: %d, Timer: %d. Interp: %.2f, Ratio: %.2f, SystemTime: %u, Prev: %s, Next: %s, Overidding: %s", nCycleIndex, nCycleTimer, g_weather.GetInterp(), GetGlobalWeatherRatio(), g_weather.GetSystemTime(), g_weather.GetPrevType().m_name, g_weather.GetNextType().m_name, g_weather.GetNetIsOverriding() ? "T" : "F"));
// global settings applied
sm_bAppliedGlobalSettings = true;
m_LastClockUpdateTime = sysTimer::GetSystemMsTime();
sm_lastDamageEventPendingOverrideEnabled = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("PED_LAST_DAMAGE_EVENT_PENDING_OVERRIDE", 0x5974776f), true);
}
void CNetwork::UpdateClockAndWeather()
{
// sync to global clock every second
static const unsigned GLOBAL_CLOCK_SYNC_MS = 1000;
// wait until we apply before updating
if(!sm_bAppliedGlobalSettings)
return;
// current time
unsigned nSysTime = sysTimer::GetSystemMsTime();
// should we update to the global time
bool bUpdateToGlobal = (nSysTime > m_NextClockSyncTime);
// update clock transition
if(m_nClockTransitionTimeLeft != 0)
{
unsigned nClockTime = m_nClockTransitionTimeLeft - (nSysTime - m_LastClockUpdateTime);
if((nClockTime == 0) || (nClockTime > m_nClockTransitionTimeLeft) || CNetwork::IsClockOverrideFromScript())
{
netTodDebug("UpdateClockAndWeather :: NetClock - Clock Transition completed - HasDebug: %s", IsUsingDebugClock() ? "False" : "True");
m_nClockTransitionTimeLeft = 0;
if(!IsUsingDebugClock() && !CNetwork::IsClockOverrideFromScript())
{
CNetwork::ClearClockTimeOverride();
// grab the global clock time
int nHour, nMinute, nSecond;
GetGlobalClock(nHour, nMinute, nSecond);
// apply
CClock::SetTime(nHour, nMinute, nSecond);
}
// flag next update
m_NextClockSyncTime = nSysTime + GLOBAL_CLOCK_SYNC_MS;
}
else
{
m_nClockTransitionTimeLeft = nClockTime;
if(!IsUsingDebugClock())
{
// get the target clock time
// grab the global clock time
int nHour, nMinute, nSecond;
GetGlobalClock(nHour, nMinute, nSecond);
Time tTimeThen(m_nClockHour, m_nClockMinute, m_nClockSecond);
Time tTimeNow(nHour, nMinute, nSecond);
float fFrom = CClock::GetDayRatio(tTimeThen);
float fTo = CClock::GetDayRatio(tTimeNow);
float fClockInterp = 1.0f - Clamp(static_cast<float>(m_nClockTransitionTimeLeft) / static_cast<float>(m_nClockAndWeatherTransitionTime), 0.0f, 1.0f);
float fRatio = Lerp(SlowInOut(fClockInterp), fFrom, fTo);
// work back to hours, minutes, seconds
CClock::FromDayRatio(fRatio, nHour, nMinute, nSecond);
CNetwork::OverrideClockTime(nHour, nMinute, nSecond, false);
}
}
}
else
{
// keep in step with the global times
if(CNetwork::IsNetworkOpen() && bUpdateToGlobal)
{
#if !__FINAL
if(CClock::HasDebugModifiedInNetworkGame() && NetworkInterface::IsHost())
{
// lock players to the debug clock so that we don't drift
netTodDebug("UpdateClockAndWeather :: CLOCK :: Debug Modified: %s, Current: %02d:%02d:%02d", NetworkInterface::IsHost() ? "Host" : "Not Host", CClock::GetHour(), CClock::GetMinute(), CClock::GetSecond());
CDebugGameClockEvent::Trigger(CDebugGameClockEvent::FLAG_IS_DEBUG_MAINTAIN);
}
else
#endif
if(CutSceneManager::GetInstance()->IsCutscenePlayingBack() || CClock::GetIsPaused())
{
netTodDebug("UpdateClockAndWeather :: CLOCK :: Cutscene: %s, Paused: %s, Current: %02d:%02d:%02d", CutSceneManager::GetInstance()->IsCutscenePlayingBack() ? "True" : "False", CClock::GetIsPaused() ? "True" : "False", CClock::GetHour(), CClock::GetMinute(), CClock::GetSecond());
}
#if !__FINAL
else if(fwTimer::IsUserPaused())
{
netTodDebug("UpdateClockAndWeather :: CLOCK :: User Paused, Current: %02d:%02d:%02d", CClock::GetHour(), CClock::GetMinute(), CClock::GetSecond());
}
#endif
else
{
// grab the global clock time
int nHour, nMinute, nSecond;
GetGlobalClock(nHour, nMinute, nSecond);
// clock logging
netTodDebug("UpdateClockAndWeather :: CLOCK :: Global: %02d:%02d:%02d, Current: %02d:%02d:%02d, Ratio: %.2f. Override: %s", nHour, nMinute, nSecond, CClock::GetHour(), CClock::GetMinute(), CClock::GetSecond(), GetGlobalClockRatio(), CNetwork::IsClockTimeOverridden() ? "True" : "False");
// stash in override store
if(CNetwork::IsClockTimeOverridden() BANK_ONLY(&& !CClock::GetTimeOverrideFlagRef()))
{
CNetwork::HandleClockUpdateDuringOverride(static_cast<int>(nHour),
static_cast<int>(nMinute),
static_cast<int>(nSecond),
fwTimer::GetTimeInMilliseconds());
}
else
{
if (!IsUsingDebugClock())
{
CClock::SetTime(nHour, nMinute, nSecond);
// grab the global date
int nGameDay;
GetGlobalDate(nGameDay);
static const s32 MULTIPLAYER_YEAR_START = 2013;
CClock::SetDate(1, Date::JANUARY, MULTIPLAYER_YEAR_START);
CClock::AddDays(nGameDay);
}
}
}
}
}
// update weather transition
if(!g_weather.IsInNetTransition())
{
if(CNetwork::IsNetworkOpen() && bUpdateToGlobal)
{
if(!g_weather.GetIsForced() && !g_weather.IsInOvertimeTransition() && !g_weather.GetNetIsOverriding())
{
// grab the global weather time
s32 nCycleIndex;
u32 nCycleTimer;
GetGlobalWeather(nCycleIndex, nCycleTimer);
// apply the global weather time
g_weather.NetworkInitFromGlobal(nCycleIndex, nCycleTimer, 0, true);
// weather logging
NOTFINAL_ONLY(netTodDebug("UpdateClockAndWeather :: WEATHER:: Global: Index: %d, Timer: %d, Interp: %.2f, Ratio: %.2f. Prev: %s, Next: %s", nCycleIndex, nCycleTimer, g_weather.GetInterp(), GetGlobalWeatherRatio(), g_weather.GetPrevType().m_name, g_weather.GetNextType().m_name);)
}
else
netTodDebug("UpdateClockAndWeather :: WEATHER :: Not applying weather. Forced: %s, OverTime: %s, Overridden: %s", g_weather.GetIsForced() ? "True" : "False", g_weather.IsInOvertimeTransition() ? "True" : "False", g_weather.GetNetIsOverriding() ? "True" : "False");
}
}
if(bUpdateToGlobal)
m_NextClockSyncTime = nSysTime + GLOBAL_CLOCK_SYNC_MS;
m_LastClockUpdateTime = nSysTime;
}
void CNetwork::StartClockTransitionToGlobal(unsigned nTransitionTime)
{
if(nTransitionTime == 0)
{
int sTransitionTime = 0;
bool bFound = Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("CLOCK_AND_WEATHER_TRANSITION_TIME", 0x8cb9ff24), sTransitionTime);
if(bFound)
nTransitionTime = static_cast<unsigned>(sTransitionTime);
else
{
static const unsigned DEFAULT_TRANSITION_TIME = 5000;
nTransitionTime = DEFAULT_TRANSITION_TIME;
}
}
// reset transition timer
m_nClockTransitionTimeLeft = nTransitionTime;
// grab the global clock time
int nHour, nMinute, nSecond;
GetGlobalClock(nHour, nMinute, nSecond);
// do not transition when we have debug time applied
if(!IsUsingDebugClock())
{
// reset transition timer
m_nClockTransitionTimeLeft = nTransitionTime;
// start overriding with current time if we're kicking off a transition
if(m_nClockTransitionTimeLeft > 0)
{
m_nClockHour = CClock::GetHour();
m_nClockMinute = CClock::GetMinute();
m_nClockSecond = CClock::GetSecond();
// apply and override the clock time to the current time
CNetwork::OverrideClockTime(m_nClockHour, m_nClockMinute, m_nClockSecond, false);
// clock logging
netTodDebug("StartClockTransitionToGlobal :: Global: %02d:%02d:%02d, Current: %02d:%02d:%02d, Ratio: %.2f, Time: %d", nHour, nMinute, nSecond, m_nClockHour, m_nClockMinute, m_nClockSecond, GetGlobalClockRatio(), m_nClockTransitionTimeLeft);
}
else
{
// clock logging
netTodDebug("StartClockTransitionToGlobal :: Instant - Global: %02d:%02d:%02d", nHour, nMinute, nSecond);
// automatically apply the final time if we have a time of 0
CClock::SetTime(nHour, nMinute, nSecond);
}
}
// refresh this
m_LastClockUpdateTime = sysTimer::GetSystemMsTime();
}
void CNetwork::StartWeatherTransitionToGlobal(unsigned nTransitionTime)
{
if(nTransitionTime == 0)
{
int sTransitionTime = 0;
bool bFound = Tunables::GetInstance().Access(CD_GLOBAL_HASH, ATSTRINGHASH("CLOCK_AND_WEATHER_TRANSITION_TIME", 0x8cb9ff24), sTransitionTime);
if(bFound)
nTransitionTime = static_cast<unsigned>(sTransitionTime);
else
{
static const unsigned DEFAULT_TRANSITION_TIME = 5000;
nTransitionTime = DEFAULT_TRANSITION_TIME;
}
}
// grab the global weather time
s32 nCycleIndex;
u32 nCycleTimer;
GetGlobalWeather(nCycleIndex, nCycleTimer);
// apply the global weather time
g_weather.NetworkInitFromGlobal(nCycleIndex, nCycleTimer, nTransitionTime);
// weather logging
netTodDebug("StartWeatherTransitionToGlobal :: Current global weather is Index: %d, Timer: %d. Ratio: %.2f", nCycleIndex, nCycleTimer, GetGlobalWeatherRatio());
}
void CNetwork::StopApplyingGlobalClockAndWeather()
{
netTodDebug("StopApplyingGlobalClockAndWeather");
sm_bAppliedGlobalSettings = false;
}
float CNetwork::GetGlobalDateRatio()
{
// apply global date - work out time for one full year in seconds
u32 nGameYear_ms = CClock::GetMsPerGameMinute() * 60 * 24 * 365;
u32 nGameYear_s = nGameYear_ms / 1000;
// current server time in seconds
u64 nTimePOSIX = rlGetPosixTime();
// work out how far through the day we are
u32 nIntoYear_s = static_cast<u32>(nTimePOSIX) % nGameYear_s;
// return as a ratio of how far through the year we are
return static_cast<float>(nIntoYear_s) / static_cast<float>(nGameYear_s);
}
void CNetwork::GetGlobalDate(int& nGameDay)
{
float fRatio = GetGlobalDateRatio();
nGameDay = static_cast<int>(365.0f * fRatio);
}
float CNetwork::GetGlobalClockRatio()
{
// work out time for one full day in seconds
u32 nGameDay_ms = CClock::GetMsPerGameMinute() * 60 * 24;
u32 nGameDay_s = nGameDay_ms / 1000;
// current server time in seconds
u64 nTimePOSIX = rlGetPosixTime();
// work out how far through the day we are
u32 nIntoDay_s = static_cast<u32>(nTimePOSIX) % nGameDay_s;
// return as a ratio of how far through the day we are
return static_cast<float>(nIntoDay_s) / static_cast<float>(nGameDay_s);
}
void CNetwork::GetGlobalClock(int& nHour, int& nMinute, int& nSecond)
{
float fRatio = GetGlobalClockRatio();
CClock::FromDayRatio(fRatio, nHour, nMinute, nSecond);
}
float CNetwork::GetGlobalWeatherRatio()
{
// current server time in seconds
u64 nTimePOSIX = rlGetPosixTime();
// time for the weather system to fully cycle (all of its cycles)
u32 nWeatherSystemTime_ms = g_weather.GetSystemTime();
u32 nWeatherSystemTime_s = nWeatherSystemTime_ms / 1000;
// work out how far through the cycle we are
u32 nIntoSystem_s = static_cast<u32>(nTimePOSIX) % nWeatherSystemTime_s;
// return as a ratio of how far through the day we are
return static_cast<float>(nIntoSystem_s) / static_cast<float>(nWeatherSystemTime_s);
}
void CNetwork::GetGlobalWeather(s32& nCycleIndex, u32& nCycleTimer)
{
float fRatio = GetGlobalWeatherRatio();
g_weather.GetFromRatio(fRatio, nCycleIndex, nCycleTimer);
}
s32 QSortEventCountersCompareFunc(const MetricEventTriggerOveruse::EventData* const eventA, const MetricEventTriggerOveruse::EventData* const eventB)
{
return ((*eventA).m_triggerCount < (*eventB).m_triggerCount) ? 1 : -1;
}
void CNetwork::UpdateEventTriggerCounters()
{
const unsigned DEFAULT_EVENT_TRIGGER_UPDATE_FREQUENCY_MS = 0;
const int DEFAULT_MAX_NUM_TRIGGERED_EVENTS_FOR_PERIOD = 10;
unsigned EVENT_TRIGGER_UPDATE_FREQUENCY_MS = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("EVENT_TRIGGER_COUNTER_FREQUENCY_MS", 0xA9331CB5), DEFAULT_EVENT_TRIGGER_UPDATE_FREQUENCY_MS);
if(EVENT_TRIGGER_UPDATE_FREQUENCY_MS != 0 && (GetSyncedTimeInMilliseconds() - sm_EventTriggerLastUpdate) > EVENT_TRIGGER_UPDATE_FREQUENCY_MS)
{
sm_EventTriggerLastUpdate = GetSyncedTimeInMilliseconds();
int MAX_NUM_TRIGGERED_EVENTS_FOR_PERIOD = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("EVENT_TRIGGER_COUNTER_LIMIT", 0x8D519F29), DEFAULT_MAX_NUM_TRIGGERED_EVENTS_FOR_PERIOD);
atFixedArray<MetricEventTriggerOveruse::EventData, netGameEvent::NETEVENTTYPE_MAXTYPES> allEventsInfo;
atFixedArray<u32, netGameEvent::NETEVENTTYPE_MAXTYPES>& eventCounters = sm_EventMgr->GetEventTriggerCounters();
bool shouldTriggerTelemetry = false;
u32 totalEventCount = 0;
for(int i = 0; i < eventCounters.GetCount(); i++)
{
MetricEventTriggerOveruse::EventData d(i, eventCounters[i]);
allEventsInfo.Push(d);
if(eventCounters[i] >= MAX_NUM_TRIGGERED_EVENTS_FOR_PERIOD)
{
shouldTriggerTelemetry = true;
}
totalEventCount += eventCounters[i];
eventCounters[i] = 0;
}
if(shouldTriggerTelemetry)
{
allEventsInfo.QSort(0, -1, QSortEventCountersCompareFunc);
atFixedArray<MetricEventTriggerOveruse::EventData, MetricEventTriggerOveruse::MAX_EVENTS_REPORTED> topEvents;
for(int i = 0; i < MetricEventTriggerOveruse::MAX_EVENTS_REPORTED; i++)
{
atHashString eventNameHash = atHashString(NetworkInterface::GetEventManager().GetEventNameFromType((NetworkEventType)allEventsInfo[i].m_eventType));
allEventsInfo[i].m_eventHash = eventNameHash.GetHash();
gnetDebug3("[EventTelemetry] triggering telemetry, adding event type: %d with hash: %d", allEventsInfo[i].m_eventType, allEventsInfo[i].m_eventHash);
topEvents.Push(allEventsInfo[i]);
}
Vector3 playerPos(0,0,0);
u32 playerRank = 0;
if(netPlayer* localPlayer = netInterface::GetLocalPlayer())
{
playerRank = SafeCast(CNetGamePlayer, localPlayer)->GetCharacterRank();
}
if(CPed* player = CPedFactory::GetFactory()->GetLocalPlayer())
{
playerPos = VEC3V_TO_VECTOR3(player->GetTransform().GetPosition());
}
CNetworkTelemetry::AppendEventTriggerOveruse(playerPos,
playerRank,
MAX_NUM_TRIGGERED_EVENTS_FOR_PERIOD,
EVENT_TRIGGER_UPDATE_FREQUENCY_MS,
totalEventCount,
topEvents);
}
}
}
void CNetwork::UpdateScuiHotkeys()
{
#if RSG_PC
CControl& control = CControlMgr::GetPlayerMappingControl();
// If the SCUI is disalbed for shutdown, do not attempt to show the UI.
// If the Frontend Social Club button is pressed on the controller, do not attempt to show the UI. Keyboard only.
// If the KEY_HOME is used, ignore it because this is handled in the RGSC SDK.
if ( !CApp::IsScuiDisabledForShutdown() &&
g_rlPc.GetUiInterface() &&
g_rlPc.GetUiInterface()->IsHotkeyEnabled() &&
!control.IsMappingInProgress() && CControlMgr::GetMainFrontendControl().GetFrontendSocialClubSecondary().IsPressed() &&
CControlMgr::GetMainFrontendControl().GetFrontendSocialClubSecondary().GetSource().m_DeviceIndex == ioSource::IOMD_KEYBOARD_MOUSE &&
CControlMgr::GetMainFrontendControl().GetFrontendSocialClubSecondary().GetSource().m_Parameter != KEY_HOME)
{
g_rlPc.ShowUi();
}
#endif
}
#if RSG_ORBIS
void CNetwork::InitialiseLiveStreaming()
{
int sceRet = sceSysmoduleLoadModule(SCE_SYSMODULE_GAME_LIVE_STREAMING);
if (gnetVerifyf(!sm_bLiveStreamingInitialised, "Live streaming already initialised") &&
gnetVerifyf(sceRet == SCE_OK, "Failed to load live streaming module. Error code: 0x%x", sceRet))
{
sceRet = sceGameLiveStreamingInitialize(SCE_GAME_LIVE_STREAMING_HEAP_SIZE);
gnetAssertf(sceRet != SCE_GAME_LIVE_STREAMING_ERROR_OUT_OF_MEMORY, "Couldn't initialise live streaming - out of memory");
if (sceRet == SCE_OK)
{
sm_bLiveStreamingInitialised = true;
EnableLiveStreaming(true);
}
}
}
void CNetwork::ShutdownLiveStreaming()
{
int sceRet = 0;
if (sm_bLiveStreamingInitialised)
{
sceRet = sceGameLiveStreamingTerminate();
if (gnetVerifyf(sceRet == SCE_OK, "Failed to terminate live streaming module. Error code: 0x%x", sceRet))
{
sceRet = sceSysmoduleUnloadModule(SCE_SYSMODULE_GAME_LIVE_STREAMING);
gnetAssertf(sceRet == SCE_OK, "Failed to unload live streaming module. Error code: 0x%x", sceRet);
}
sm_bLiveStreamingInitialised = false;
}
}
void CNetwork::EnableLiveStreaming(bool bEnable)
{
if (gnetVerifyf(sm_bLiveStreamingInitialised == true, "Called EnableLiveStreaming when live streaming isn't initialised"))
{
NET_ASSERTS_ONLY(int sceRet = )sceGameLiveStreamingEnableLiveStreaming(bEnable);
gnetAssertf(sceRet == SCE_OK, "Failed to enable/disable live streaming. Error code: 0x%x", sceRet);
}
}
bool CNetwork::IsLiveStreamingEnabled()
{
if (sm_bLiveStreamingInitialised == false)
{
return false;
}
SceGameLiveStreamingStatus status;
int sceRet = sceGameLiveStreamingGetCurrentStatus(&status);
if (gnetVerifyf(sceRet == SCE_OK, "Failed to get status of live streaming module. Error code: 0x%x", sceRet))
{
return status.isOnAir;
}
return false;
}
#define MIN_VERSION_FOR_PROSPERO_CHECK (0x08000000u)
bool CNetwork::IsPlayingInBackCompatOnPS5()
{
#if !RSG_FINAL
if (PARAM_netForceBackCompatAsPS5.Get() || PARAM_PS4BackwardsCompatibility.Get())
return true;
#endif
// assume default of true if online (in retail, this means we have the latest system software)
bool allowProsperoCheck = NetworkInterface::IsLocalPlayerOnline();
#if !RSG_FINAL
// in non-final, possible that some kits are not on the recommended software version (which will cause a PRX failure on calling sceKernelIsProspero)
// so check that the version is valid here
static bool s_HasCheckedSystemSoftware = false;
static bool s_SystemSoftwareResult = false;
if (!s_HasCheckedSystemSoftware)
{
char sysversion[SCE_SYSTEM_SOFTWARE_VERSION_LEN + 1];
if (SCE_OK == sceDbgGetSystemSwVersion(sysversion, SCE_SYSTEM_SOFTWARE_VERSION_LEN + 1))
{
u32 hexver = 0;
for (int i = SCE_SYSTEM_SOFTWARE_VERSION_LEN, j = 0; i >= 0; i--)
if (sysversion[i] >= '0' && sysversion[i] <= '9')
hexver += (sysversion[i] - '0') * pow(16, j++);
// this check is only valid on software above MIN_VERSION_FOR_PROSPERO_CHECK
s_SystemSoftwareResult = (hexver > MIN_VERSION_FOR_PROSPERO_CHECK);
gnetDebug1("IsPlayingInBackCompatOnPS5 :: SystemVersion: %s [0x%08x], AllowCheck: %s", sysversion, hexver, s_SystemSoftwareResult ? "True" : "False");
s_HasCheckedSystemSoftware = true;
}
}
allowProsperoCheck = s_SystemSoftwareResult;
#endif
// if allowed, allow a tunable kill-switch for this
if (allowProsperoCheck && Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("PS4_ALLOW_PROSPERO_CHECK", 0x3fd9b373), true))
{
static bool s_HasCheckedProspero = false;
static bool s_ProsperoCheckResult = false;
if (!s_HasCheckedProspero)
{
s_ProsperoCheckResult = sceKernelIsProspero();
gnetDebug1("IsPlayingInBackCompatOnPS5 :: Prospero: %s", s_ProsperoCheckResult ? "True" : "False");
s_HasCheckedProspero = true;
}
return s_ProsperoCheckResult;
}
return false;
}
#endif // RSG_ORBIS
static const unsigned MAX_TRACKER_PARALLEL_STAGES = 3;
unsigned s_TrackerLastStageTimestamp[MAX_TRACKER_PARALLEL_STAGES] = { 0, 0, 0 };
static MetricTransitionTrack s_TrackerTelemetryMetric;
#if !__NO_OUTPUT
static unsigned s_TrackerStartTimestamp = 0;
static unsigned s_TrackerStageCount[MAX_TRACKER_PARALLEL_STAGES] = { 0, 0, 0 };
#endif
void CNetwork::StartTransitionTracker(int nTransitionType, int nContextParam1, int nContextParam2, int nContextParam3)
{
#if !__NO_OUTPUT
if(s_TrackerStartTimestamp != 0)
{
netTransitionAssertf(0, "AddStage :: %s - Haven't called NETWORK_TRANSITION_FINISH", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
netTransitionDebug("Start :: %s - Type: %u, Params: %u, %u, %u", CTheScripts::GetCurrentScriptNameAndProgramCounter(), nTransitionType, nContextParam1, nContextParam2, nContextParam3);
s_TrackerStartTimestamp = sysTimer::GetSystemMsTime();
#endif
s_TrackerTelemetryMetric.Start(nTransitionType, nContextParam1, nContextParam2, nContextParam3);
}
void CNetwork::AddTransitionTrackerStage(int nStageSlot, int nStageHash, int nContextParam1, int nContextParam2, int nContextParam3)
{
unsigned nPrevStageTime = s_TrackerLastStageTimestamp[nStageSlot] > 0 ? sysTimer::GetSystemMsTime() - s_TrackerLastStageTimestamp[nStageSlot] : 0;
#if !__NO_OUTPUT
if((nStageSlot < 0) || (nStageSlot >= MAX_TRACKER_PARALLEL_STAGES))
{
netTransitionAssertf(0, "AddStage :: %s - Invalid Stage Slot: %d", CTheScripts::GetCurrentScriptNameAndProgramCounter(), nStageSlot);
return;
}
if(s_TrackerStartTimestamp == 0)
{
netTransitionAssertf(0, "AddStage :: %s - Haven't called NETWORK_TRANSITION_START", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
s_TrackerStageCount[nStageSlot]++;
netTransitionDebug("\tAddStage :: %s: Slot: %u, Count: %u, Hash: 0x%08x, Params: %u, %u, %u, Last Stage: %ums, Total: %ums", CTheScripts::GetCurrentScriptNameAndProgramCounter(), nStageSlot, s_TrackerStageCount[nStageSlot], nStageHash, nContextParam1, nContextParam2, nContextParam3, nPrevStageTime, sysTimer::GetSystemMsTime() - s_TrackerStartTimestamp);
#endif
s_TrackerLastStageTimestamp[nStageSlot] = sysTimer::GetSystemMsTime();
if(!s_TrackerTelemetryMetric.AddStage(nStageSlot, nStageHash, nContextParam1, nContextParam2, nContextParam3, nPrevStageTime))
{
#if !__NO_OUTPUT
netTransitionDebug("\tAddStage :: Writing telemetry (Length: %u):", s_TrackerTelemetryMetric.GetLength());
s_TrackerTelemetryMetric.LogContents();
#endif
if(s_SendTransitionTrackerTelemetry)
{
APPEND_METRIC_FLUSH(s_TrackerTelemetryMetric, false);
}
s_TrackerTelemetryMetric.ClearStages();
gnetVerify(s_TrackerTelemetryMetric.AddStage(nStageSlot, nStageHash, nContextParam1, nContextParam2, nContextParam3, nPrevStageTime));
}
}
void CNetwork::FinishTransitionTracker(int OUTPUT_ONLY(nContextParam1), int OUTPUT_ONLY(nContextParam2), int OUTPUT_ONLY(nContextParam3))
{
#if !__NO_OUTPUT
if(s_TrackerStartTimestamp == 0)
{
netTransitionAssertf(0, "AddStage :: %s - Haven't called NETWORK_TRANSITION_START", CTheScripts::GetCurrentScriptNameAndProgramCounter());
}
netTransitionDebug("Finish :: %s: Total Time: %u, Params: %u, %u, %u", CTheScripts::GetCurrentScriptNameAndProgramCounter(), sysTimer::GetSystemMsTime() - s_TrackerStartTimestamp, nContextParam1, nContextParam2, nContextParam3);
s_TrackerStartTimestamp = 0;
#endif
for(unsigned i = 0; i < MAX_TRACKER_PARALLEL_STAGES; i++)
{
s_TrackerLastStageTimestamp[i] = 0;
#if !__NO_OUTPUT
s_TrackerStageCount[i] = 0;
#endif
}
// write what we have left
s_TrackerTelemetryMetric.Finish();
#if !__NO_OUTPUT
netTransitionDebug("Finish :: Writing telemetry (Length: %u):", s_TrackerTelemetryMetric.GetLength());
s_TrackerTelemetryMetric.LogContents();
#endif
if(s_SendTransitionTrackerTelemetry)
{
APPEND_METRIC_FLUSH(s_TrackerTelemetryMetric, false);
}
}
void
CNetwork::ClearTransitionTracker()
{
#if !__NO_OUTPUT
netTransitionDebug("ClearTransitionTracker");
s_TrackerStartTimestamp = 0;
#endif
for(unsigned i = 0; i < MAX_TRACKER_PARALLEL_STAGES; i++)
{
s_TrackerLastStageTimestamp[i] = 0;
#if !__NO_OUTPUT
s_TrackerStageCount[i] = 0;
#endif
}
}
#if !__FINAL
bool CNetwork::BypassMultiplayerAccessChecks()
{
return PARAM_netBypassMultiplayerAccessChecks.Get() || PARAM_mpavailable.Get();
}
#endif
bool CNetwork::IsMultiplayerDisabled()
{
if(Tunables::GetInstance().HasCloudTunables())
return Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("MULTIPLAYER_DISABLED", 0x3c0af527), false);
return false;
}
bool CNetwork::IsMultiplayerLocked()
{
#if !__FINAL
if(BypassMultiplayerAccessChecks())
return false;
#endif
return sm_bMultiplayerLocked;
}
void CNetwork::SetMultiplayerLocked(const bool multiplayerLocked)
{
if(sm_bMultiplayerLocked != multiplayerLocked)
{
gnetDebug1("SetMultiplayerLocked :: %s -> %s", sm_bMultiplayerLocked ? "True" : "False", multiplayerLocked ? "True" : "False");
sm_bMultiplayerLocked = multiplayerLocked;
}
}
void CNetwork::SetSpPrologueRequired(const bool bIsRequired)
{
if(sm_bSpPrologueRequired != bIsRequired)
{
gnetDebug1("SetSpPrologueRequired :: Setting: %s", bIsRequired ? "True" : "False");
sm_bSpPrologueRequired = bIsRequired;
}
}
bool CNetwork::IsSpPrologueRequired()
{
#if !__FINAL
int nCommandValue;
if(PARAM_netRequireSpPrologue.Get(nCommandValue))
return nCommandValue != 0;
if(BypassMultiplayerAccessChecks())
return false;
#endif
return sm_bSpPrologueRequired;
}
IntroSettingResult CNetwork::HasCompletedSpPrologue()
{
#if !RSG_FINAL
int nCommandValue;
if(PARAM_netSpPrologueComplete.Get(nCommandValue))
return static_cast<IntroSettingResult>(nCommandValue);
#endif
// check profile settings are valid
CProfileSettings& settings = CProfileSettings::GetInstance();
if(!settings.AreSettingsValid())
{
return IntroSettingResult::Result_Unknown;
}
// profile settings - check prologue
if(!settings.Exists(CProfileSettings::PROLOGUE_COMPLETE))
{
return IntroSettingResult::Result_Unknown;
}
// get the value
return static_cast<IntroSettingResult>(settings.GetInt(CProfileSettings::PROLOGUE_COMPLETE));
}
bool CNetwork::SetCompletedSpPrologue(const bool bHasCompleted)
{
// check profile settings are valid
CProfileSettings& settings = CProfileSettings::GetInstance();
if(!settings.AreSettingsValid())
{
return false;
}
gnetDebug1("SetCompletedSpPrologue :: Completed: %s", bHasCompleted ? "True" : "False");
settings.Set(CProfileSettings::PROLOGUE_COMPLETE, bHasCompleted ? IntroSettingResult::Result_Complete : IntroSettingResult::Result_NotComplete);
settings.Write();
return true;
}
bool CNetwork::CheckSpPrologueRequirement()
{
// combines checks for required and completed
return !IsSpPrologueRequired() || (HasCompletedSpPrologue() == IntroSettingResult::Result_Complete);
}
bool CNetwork::IsLastDamageEventPendingOverrideEnabled()
{
return sm_lastDamageEventPendingOverrideEnabled;
}
#if !__NO_OUTPUT
const char* s_AccessAreaString[] =
{
"AccessArea_Landing",
"AccessArea_Multiplayer",
"AccessArea_MultiplayerEnter",
};
CompileTimeAssert(COUNTOF(s_AccessAreaString) == eNetworkAccessArea::AccessArea_Num);
#endif
bool IsMultiplayerArea(const eNetworkAccessArea accessArea)
{
switch(accessArea)
{
case AccessArea_Multiplayer:
case AccessArea_Landing:
default:
return true;
}
}
bool CNetwork::IsAccessCodeApplicableToArea(const eNetworkAccessCode nAccessCode, const eNetworkAccessArea nAccessArea)
{
// no default, we want a compile failure for unhandled codes
switch (nAccessCode)
{
// the return value indicates whether an error code is expected in the alert string
case eNetworkAccessCode::Access_BankDefault: return false;
case eNetworkAccessCode::Access_Invalid: return false;
case eNetworkAccessCode::Access_Deprecated_TunableFalse: return false;
case eNetworkAccessCode::Access_Deprecated_TunableNotFound: return false;
case eNetworkAccessCode::Access_Granted: return true;
case eNetworkAccessCode::Access_Denied_MultiplayerLocked: return IsMultiplayerArea(nAccessArea) && (nAccessArea != eNetworkAccessArea::AccessArea_Landing);
case eNetworkAccessCode::Access_Denied_MultiplayerDisabled: return IsMultiplayerArea(nAccessArea);
#if RSG_PC
// on PC, we can create a profile on the SCUI flow so we're not required to be signed in here
case eNetworkAccessCode::Access_Denied_NotSignedIn: return true; // (nAccessArea != eNetworkAccessArea::AccessArea_SocialClub);
case eNetworkAccessCode::Access_Denied_NotSignedOnline: return true; // (nAccessArea != eNetworkAccessArea::AccessArea_SocialClub);
#else
case eNetworkAccessCode::Access_Denied_NotSignedIn: return true;
case eNetworkAccessCode::Access_Denied_NotSignedOnline: return true;
#endif
case eNetworkAccessCode::Access_Denied_NoNetworkAccess: return (nAccessArea == eNetworkAccessArea::AccessArea_Landing);
//case eNetworkAccessCode::Access_Denied_NoRosCredentials: return true;
#if RSG_GGP
// on GGP, we cannot access multiplayer menus when we do not have the multiplayer / online privilege
case eNetworkAccessCode::Access_Denied_NoOnlinePrivilege: return IsMultiplayerArea(nAccessArea);
#else
case eNetworkAccessCode::Access_Denied_NoOnlinePrivilege: return IsMultiplayerArea(nAccessArea) && (nAccessArea != eNetworkAccessArea::AccessArea_Landing);
#endif
// Script can't handle Access_Denied_NoRosCredentials so we only track it for the landing page right now
case eNetworkAccessCode::Access_Denied_NoRosCredentials: return nAccessArea == eNetworkAccessArea::AccessArea_Landing;
case eNetworkAccessCode::Access_Denied_NoRosPrivilege: return IsMultiplayerArea(nAccessArea);
// these are specific to the multiplayer enter, we have a transition module which waits for these to complete
case eNetworkAccessCode::Access_Denied_NoTunables: return (nAccessArea == eNetworkAccessArea::AccessArea_MultiplayerEnter);
case eNetworkAccessCode::Access_Denied_NoBackgroundScript: return (nAccessArea == eNetworkAccessArea::AccessArea_MultiplayerEnter);
// these should only fail on the enter - covers the invite case where we bypass the landing screen but fail the invite
// we still need to verify that the policies succeeded in this flow
//case eNetworkAccessCode::Access_Denied_PoliciesNotAccepted: return (nAccessArea == eNetworkAccessArea::AccessArea_MultiplayerEnter);
//case eNetworkAccessCode::Access_Denied_PoliciesDownloadFailed: return (nAccessArea == eNetworkAccessArea::AccessArea_MultiplayerEnter);
case eNetworkAccessCode::Access_Denied_InvalidProfileSettings: return IsMultiplayerArea(nAccessArea);
case eNetworkAccessCode::Access_Denied_PrologueIncomplete: return IsMultiplayerArea(nAccessArea);
case eNetworkAccessCode::Access_Denied_RosBanned: return (nAccessArea != eNetworkAccessArea::AccessArea_MultiplayerEnter);
case eNetworkAccessCode::Access_Denied_RosSuspended: return (nAccessArea != eNetworkAccessArea::AccessArea_MultiplayerEnter);
//case eNetworkAccessCode::Access_Denied_InvalidSandbox: return true;
//case eNetworkAccessCode::Access_Denied_NoXstsToken: return true;
//case eNetworkAccessCode::Access_Denied_NetworkManifestError: return (nAccessArea == eNetworkAccessArea::AccessArea_MultiplayerEnter);
//case eNetworkAccessCode::Access_Denied_AgeRestricted: return (nAccessArea != eNetworkAccessArea::AccessArea_Store);
//case eNetworkAccessCode::Access_Denied_GameUpdateRequired: return true;
//case eNetworkAccessCode::Access_Denied_SystemUpdateRequired: return true;
//case eNetworkAccessCode::Access_Denied_ServiceUnreachable: return true;
//case eNetworkAccessCode::Access_Denied_NoRelayServerCxn: return IsMultiplayerArea(nAccessArea) && (nAccessArea != eNetworkAccessArea::AccessArea_MultiplayerLanding);
//case eNetworkAccessCode::Access_Denied_IncompatibleCommandLine: return true;
//case eNetworkAccessCode::Access_Denied_IncompatibleHostsFile: return true;
//case eNetworkAccessCode::Access_Denied_RateLimitCloudConfigFailed: return (nAccessArea == eNetworkAccessArea::AccessArea_MultiplayerEnter);
}
return false;
}
#if !__NO_OUTPUT
int s_BankNetworkAccessCode = eNetworkAccessCode::Access_BankDefault;
bool s_BankForceReadyForAccessCheck = false;
bool s_BankForceNotReadyForAccessCheck = false;
#endif
bool CNetwork::IsReadyToCheckNetworkAccess(const eNetworkAccessArea OUTPUT_ONLY(accessArea), IsReadyToCheckAccessResult* pResult)
{
#define IS_READY_RESULT_COMMON(x) \
if(pResult) \
*pResult = x; \
return (x >= IsReadyToCheckAccessResult::Result_Ready_First) && (x <= IsReadyToCheckAccessResult::Result_Ready_Last);
#if RSG_OUTPUT
// keep track of the last result for each area
static IsReadyToCheckAccessResult s_LastResult[] =
{
IsReadyToCheckAccessResult::Result_Invalid,
IsReadyToCheckAccessResult::Result_Invalid,
IsReadyToCheckAccessResult::Result_Invalid,
};
CompileTimeAssert(COUNTOF(s_LastResult) == eNetworkAccessArea::AccessArea_Num);
#define IS_READY_RESULT(x) \
if(s_LastResult[accessArea] != x) \
{ \
gnetDebug1("IsReadyToCheckNetworkAccess :: %s - %s", s_AccessAreaString[accessArea], #x); \
s_LastResult[accessArea] = x; \
} \
IS_READY_RESULT_COMMON(x);
#else
// nothing to do in non-logging builds except bail out
#define IS_READY_RESULT(x) \
IS_READY_RESULT_COMMON(x);
#endif
#if !RSG_FINAL
if(s_BankForceReadyForAccessCheck)
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_Ready_CommandLine);
}
else if(s_BankForceNotReadyForAccessCheck)
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_CommandLine);
}
#endif
const int localGamerIndex = NetworkInterface::GetLocalGamerIndex();
// if we've not been assigned a valid user index, we can check (and fail)
if(!RL_IS_VALID_LOCAL_GAMER_INDEX(localGamerIndex))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_Ready_InvalidLocalGamerIndex);
}
// if we've recently resumed the game, allow the system a window of time to sort itself out.
if(CLiveManager::HasRecentResume())
{
// we can early out of this window if we've gained network access, signed online and received credentials
bool skipResumeCheck =
NetworkInterface::IsLocalPlayerOnline() &&
NetworkInterface::HasNetworkAccess() &&
NetworkInterface::HasValidRosCredentials();
if(!skipResumeCheck)
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_RecentlyResumed);
}
}
// if we don't have an internet connection, we can check (and fail)
if(!NetworkInterface::HasNetworkAccess())
{
return true;
}
#if RSG_NP
// check if we're still awaiting an NP result
if (!g_rlNp.HasAvailabilityResult(localGamerIndex))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_WaitingForAvailabilityResult);
}
// if not, and Np is unavailable, we can check (and fail)
if (!g_rlNp.IsNpAvailable(localGamerIndex))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_Ready_NpUnavailable);
}
#endif
#if RSG_PC
// On PC, wait for sign-in to complete.
if (g_rlPc.GetProfileManager()->IsSigningIn())
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_WaitingForSignInCompletion);
}
#endif
// check if we're waiting for ROS credentials. On Social Club platforms, the lack of
// platform credentials indicates the user is offline.
const rlRosLoginStatus loginStatus = rlRos::GetLoginStatus(localGamerIndex);
if(loginStatus == RLROS_LOGIN_STATUS_IN_PROGRESS
NON_SC_PLATFORMS_ONLY(|| loginStatus == RLROS_LOGIN_STATUS_NEED_PLATFORM_CREDENTIALS))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_WaitingForRosCredentials);
}
#define RSG_SUPPORT_SOCIAL_CLUB_LANDING (0)
#if RSG_SUPPORT_SOCIAL_CLUB_LANDING
// to this point, we've checked everything we need to for Social Club
if((accessArea == AccessArea_SocialClub) || (accessArea == AccessArea_Cloud))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_Ready_PassedAllChecks);
}
#endif
#define RSG_SUPPORT_LOCKING (0)
#if RSG_SUPPORT_LOCKING
// if we're requesting access to an area that can fail due to Access_Denied_MultiplayerLocked
// make sure that the unlock state is ready to be checked
if(IsAccessCodeApplicableToArea(eNetworkAccessCode::Access_Denied_MultiplayerLocked, accessArea) && !IsReadyToCheckUnlockState())
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_NotReadyToCheckUnlockState);
}
#endif
// disabled this for now - need to add support that we ever had a result notification
#if RSG_XBL && 0
// for multiplayer areas, we need to wait for the multiplayer permission result (check HasFiredPrivilegeResult which indicates
// that the game layer will have been told about the result - our privilege checks use the cached copies in NetworkPrivileges)
if(IsMultiplayerArea(accessArea))
{
IPrivileges* privilegesApi = g_rlXbl.GetPrivilegesManager();
if (privilegesApi && !privilegesApi->GetPrivilegeCheckResult(localGamerIndex, rlPrivileges::PRIVILEGE_MULTIPLAYER_SESSIONS))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_WaitingForPrivilegeResult);
}
}
#endif
#if RSG_NP
// wait for NP permissions / PS+ check to complete
if(!g_rlNp.GetNpAuth().HasRetrievedPermissions(localGamerIndex))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_WaitingForPermissions);
}
// if we don't have PS+, we can still qualify for online play via a PS+ event
// wait for the tunables / event file to come down from the cloud before proceeding
// these files are both required in order to determine if we are able to able to bypass PS+ gating
if(!g_rlNp.GetNpAuth().IsPlusAuthorized(localGamerIndex) &&
(!SocialClubEventMgr::Get().GetDataReceived() || !Tunables::IsInstantiated() || !Tunables::GetInstance().HasCloudTunables()))
{
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_NotReady_WaitingForPlusEvent);
}
#endif
// all checks passed
IS_READY_RESULT(IsReadyToCheckAccessResult::Result_Ready_PassedAllChecks);
}
eNetworkAccessCode CNetwork::CheckNetworkAccess(const eNetworkAccessArea nAccessArea, u64* endPosixTime)
{
if(endPosixTime != nullptr)
*endPosixTime = 0;
if(!gnetVerify(nAccessArea >= eNetworkAccessArea::AccessArea_First && nAccessArea < eNetworkAccessArea::AccessArea_Num))
{
gnetError("CheckNetworkAccess :: Invalid Access Area supplied!");
return eNetworkAccessCode::Access_Invalid;
}
#if RSG_OUTPUT
// keep track of the last code
static eNetworkAccessCode s_Code[] =
{
eNetworkAccessCode::Access_Invalid,
eNetworkAccessCode::Access_Invalid,
eNetworkAccessCode::Access_Invalid,
};
CompileTimeAssert(COUNTOF(s_Code) == eNetworkAccessArea::AccessArea_Num);
#endif
#define NETWORK_ACCESS_RESULT_COMMON(x) \
return (x);
#if RSG_OUTPUT
#define NETWORK_ACCESS_RESULT(x, y) \
if(s_Code[nAccessArea] != x) \
{ \
gnetDebug1("CheckNetworkAccess :: %s - %s, %s (%s)", (x == eNetworkAccessCode::Access_Granted) ? "Yes" : "No", s_AccessAreaString[nAccessArea], #x, y); \
s_Code[nAccessArea] = x; \
} \
NETWORK_ACCESS_RESULT_COMMON(x)
#else
#define NETWORK_ACCESS_RESULT(x, y) NETWORK_ACCESS_RESULT_COMMON(x)
#endif
#define NETWORK_ACCESS_CHECK(x, y, z) \
if(IsAccessCodeApplicableToArea(x, nAccessArea) && (y)) \
{ \
NETWORK_ACCESS_RESULT(x, z) \
}
#if !RSG_FINAL
if(s_BankNetworkAccessCode != eNetworkAccessCode::Access_BankDefault)
{
NETWORK_ACCESS_RESULT(static_cast<eNetworkAccessCode>(s_BankNetworkAccessCode), "Using -netMpAccessCode");
}
#endif
// add this check first so that we display the appropriate alert
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_MultiplayerLocked, NetworkInterface::IsMultiplayerLocked(), "Multiplayer Locked");
// for landing area specifically, we want to let the player through when multiplayer is locked (and skip any further checks)
// so that we show our specific UI related to this
if(nAccessArea == eNetworkAccessArea::AccessArea_Landing && IsMultiplayerLocked())
{
NETWORK_ACCESS_RESULT(eNetworkAccessCode::Access_Granted, "Allowing Access to Landing when Multiplayer Locked")
}
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoNetworkAccess, !NetworkInterface::HasNetworkAccess(), "No Network Access");
#if RSG_XBL
// if our sandbox is invalid, all remote access is broken
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_InvalidSandbox, !rlGetTitleId()->GetXblTitleId()->IsSandboxIdValid(), (char*)rlGetTitleId()->GetXblTitleId()->GetSandboxId());
#endif
#if RSG_OUTPUT
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_IncompatibleCommandLine, NetworkInterface::Live::HasIncompatibleCommandLines(), "Incompatible Network Command Line");
#endif
#if RSG_PC && RSG_OUTPUT
//if((!NetworkInterface::IsLocalPlayerOnline() || !NetworkInterface::IsLocalPlayerSignedIn()))
//{
// NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_IncompatibleHostsFile, !rlCheckHostsFile(rlGetTitleId()->GetRosTitleId()->GetEnvironmentName()), "Hosts file not set up correctly");
//}
#endif
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_GameUpdateRequired, NetworkInterface::Live::IsGameUpdateRequired(), "Game Update Required");
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_SystemUpdateRequired, NetworkInterface::Live::IsSystemUpdateRequired(), "System Update Required");
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_AgeRestricted, NetworkInterface::Live::LocalPlayerHasAgeRestrictions(), "Restricted by Age");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NotSignedIn, !NetworkInterface::IsLocalPlayerSignedIn(), "Not Signed In");
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoNetworkAccess, !NetworkInterface::Live::HasNetworkAccess(), "No Network Access");
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_ServiceUnreachable, NetworkInterface::Live::IsOnlineServiceUnreachable(), "Online Service Unreachable");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NotSignedOnline, !NetworkInterface::IsLocalPlayerOnline(), "Not Signed Online");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoOnlinePrivilege, !NetworkInterface::CheckOnlinePrivileges(),"No Online Privileges");
#define NET_ENABLE_XBL_CONFIG_CHECKS (0)
#if NET_ENABLE_XBL_CONFIG_CHECKS && RSG_XDK && RSG_BANK
// if we don't have an SC ticket, check if it's because we can't get an XSTS token
int xstsTokenResult = 0;
if(!NetworkInterface::HasValidRosCredentials() && !rlPresence::GetXblTokenResult(&xstsTokenResult))
{
char xstsTokenError[128] = {0};
formatf(xstsTokenError, "No ROS credentials. Failed to retrieve an XSTS token. Error code: 0x%08x", xstsTokenResult);
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoXstsToken, !NetworkInterface::HasValidRosCredentials(), xstsTokenError);
}
else
#endif
{
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoRosCredentials, !NetworkInterface::HasValidRosCredentials(), "No ROS credentials");
}
#if NET_ENABLE_XBL_CONFIG_CHECKS && RSG_XDK
const u32 networkManifestResultCode = NetworkInterface::Live::GetNetworkManifestResultCode();
if((networkManifestResultCode != 0) && !NetworkInterface::Live::HasRelayServerConnection() && !NetworkInterface::Live::HasPresenceServerConnection())
{
// an exception occurred while loading the network manifest, and it appears that our main socket port is blocked
char manifestError[128] = {0};
formatf(manifestError, "Failed to load the network manifest. Error code: 0x%08x", networkManifestResultCode);
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NetworkManifestError, networkManifestResultCode != 0, manifestError);
}
#endif
#define NET_MANAGE_BANNED_STATUS (1)
#if NET_MANAGE_BANNED_STATUS
if(NetworkInterface::HasValidRosCredentials())
{
bool granted = false;
const rlRosCredentials &credentials = rlRos::GetCredentials(NetworkInterface::GetLocalGamerIndex());
const bool hasEndDate = credentials.HasPrivilegeEndDate(RLROS_PRIVILEGEID_BANNED, &granted, endPosixTime);
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_RosBanned, NetworkInterface::HasRosPrivilege(RLROS_PRIVILEGEID_BANNED) && !hasEndDate, "Banned account");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_RosSuspended, NetworkInterface::HasRosPrivilege(RLROS_PRIVILEGEID_BANNED) && hasEndDate, "Suspended account");
}
#endif
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoRosPrivilege, NetworkInterface::HasValidRosCredentials() && !NetworkInterface::HasRosPrivilege(RLROS_PRIVILEGEID_MULTIPLAYER), "No ROS Multiplayer privilege");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoRosPrivilege, NetworkInterface::HasValidRosCredentials() && !NetworkInterface::HasRosPrivilege(RLROS_PRIVILEGEID_CLOUD_STORAGE_READ), "No ROS CloudRead privilege");
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoRosPrivilege, !NetworkInterface::HasRosPrivilege(RLROS_PRIVILEGEID_CONDUCTOR), "No ROS Conductor privilege");
#if !__FINAL
// this skips any of the softer requirements for multiplayer access
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Granted, BypassMultiplayerAccessChecks(), "BypassMultiplayerAccessChecks")
#endif
#define NET_MANAGE_POLICY (0)
#if NET_MANAGE_POLICY
#if !RSG_FINAL
if(!CLandingPage::ShouldShowOnBoot())
#endif
{
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_PoliciesNotAccepted, SCPolicyManager::GetInstance().NeedsPoliciesAcceptance(), "Policies not accepted");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_PoliciesDownloadFailed, !SCPolicyManager::GetInstance().IsInitialized(nullptr), "Policies download failed");
}
#endif
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_PrologueIncomplete, !CheckSpPrologueRequirement(), "Prologue not Complete");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_MultiplayerDisabled, IsMultiplayerDisabled(), "Multiplayer Disabled");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoTunables, !Tunables::GetInstance().HasCloudTunables() NOTFINAL_ONLY(&& !PARAM_netMpSkipCloudFileRequirements.Get()), "Invalid Tunables");
//NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_RateLimitCloudConfigFailed, !netHttpRateLimiter::HasConfigFile(), "Rate limit config failed");
NETWORK_ACCESS_CHECK(eNetworkAccessCode::Access_Denied_NoBackgroundScript, !SBackgroundScripts::GetInstance().HasCloudScripts() NOTFINAL_ONLY(&& !PARAM_netMpSkipCloudFileRequirements.Get()), "Invalid Background Scripts");
// all checks pass, access granted
NETWORK_ACCESS_RESULT(eNetworkAccessCode::Access_Granted, "All checks passed");
}
bool CNetwork::CanAccessNetworkFeatures(const eNetworkAccessArea accessArea, eNetworkAccessCode* pAccessCode)
{
const eNetworkAccessCode accessCode = CheckNetworkAccess(accessArea);
if(pAccessCode)
*pAccessCode = accessCode;
return accessCode == eNetworkAccessCode::Access_Granted;
}
eNetworkAccessCode CNetwork::CheckNetworkAccessAndPopulateErrorDetails(const eNetworkAccessArea nAccessArea, sNetworkErrorDetails& errorDetails)
{
u64 nEndPosixTime;
eNetworkAccessCode nAccessCode = CheckNetworkAccess(nAccessArea, &nEndPosixTime);
if(nAccessCode == eNetworkAccessCode::Access_Granted)
{
errorDetails.Reset();
}
else
{
gnetDebug1("CheckNetworkAccess :: Cannot access!");
// throw up an alert screen
errorDetails.m_ErrorMsg = GetNetworkAccessErrorString(nAccessArea, nAccessCode);
int nErrorCode;
bool bExpectsErrorCode = GetNetworkAccessErrorCode(nAccessCode, nErrorCode);
errorDetails.m_ErrorCode = nErrorCode;
errorDetails.m_EndPosixTime = nEndPosixTime;
errorDetails.m_HasErrorCode = bExpectsErrorCode;
errorDetails.m_AlertSource = AlertScreenSource::Source_CodeAccess;
errorDetails.m_AlertContext = static_cast<u32>(nAccessCode);
#if RSG_OUTPUT
errorDetails.m_DeveloperMode = !NetworkInterface::IsUsingCompliance();
#endif
}
return nAccessCode;
}
bool CNetwork::ShowSystemUIForAccessCode(eNetworkAccessCode const nAccessCode, bool& bNeedInGameUi_Out)
{
bool bShowPlatformUI = false;
bNeedInGameUi_Out = true;
// intercept when we need to show platform UI
switch (nAccessCode)
{
case eNetworkAccessCode::Access_Denied_NoOnlinePrivilege:
{
// disable this for now, QA won't be using -netCompliance on V
#if !RSG_FINAL && 0
// in non-prod environments, we default to an in-game alert with help prompt where we can
// with guides on how to create online enabled platform accounts (instead of showing platform UI)
if(!NetworkInterface::IsUsingCompliance() && g_rlTitleId->m_RosTitleId.GetEnvironment() != RLROS_ENV_PROD)
{
gnetDebug1("ShowSystemUIForAccessCode :: Skipping Account Upsell UI in Development Environments");
break;
}
#endif
#if RSG_NP
gnetDebug1("ShowSystemUIForAccessCode :: Showing Account Upsell");
CLiveManager::ShowAccountUpgradeUI();
bShowPlatformUI = true;
bNeedInGameUi_Out = false;
#elif RSG_XBOX
gnetDebug1("ShowSystemUIForAccessCode :: Invoking Privilege Resolution");
if(CLiveManager::ResolvePlatformPrivilege(NetworkInterface::GetLocalGamerIndex(), rlPrivileges::PrivilegeTypes::PRIVILEGE_MULTIPLAYER_SESSIONS, true))
{
bShowPlatformUI = true;
bNeedInGameUi_Out = false;
}
else
{
gnetDebug1("ShowSystemUIForAccessCode :: Invoking Privilege Resolution skipped");
}
#endif
}
break;
default:
break;
}
return bShowPlatformUI;
}
bool CNetwork::CheckNetworkAccessAndAlertIfFail(const eNetworkAccessArea nAccessArea)
{
eNetworkAccessCode nAccessCode = CheckNetworkAccessAndPopulateErrorDetails(nAccessArea, s_NetworkAlertParams.m_ErrorDetails);
bool hasError = s_NetworkAlertParams.m_ErrorDetails.HasError();
if(hasError)
{
bool bNeedInGameUi = true;
ShowSystemUIForAccessCode(nAccessCode, bNeedInGameUi);
if(bNeedInGameUi)
{
// setup and update once to show the screen
SetupNetworkAlertScreen();
UpdateNetworkAlertScreen();
}
}
return !hasError;
}
const char* CNetwork::GetNetworkAccessErrorString(const eNetworkAccessArea NET_ASSERTS_ONLY(nAccessArea), const eNetworkAccessCode nAccessCode)
{
// check that the code / area match
gnetAssert(IsAccessCodeApplicableToArea(nAccessCode, nAccessArea));
// no default, we want a compile failure for unhandled codes
switch (nAccessCode)
{
// we can consider returning different messages for different areas here
case eNetworkAccessCode::Access_BankDefault: return nullptr;
case eNetworkAccessCode::Access_Invalid: return nullptr;
case eNetworkAccessCode::Access_Granted: return nullptr;
case eNetworkAccessCode::Access_Denied_MultiplayerLocked: return "ACCESS_DENIED_NETWORK_LOCKED";
// V specific
case eNetworkAccessCode::Access_Denied_InvalidProfileSettings: return "ACCESS_DENIED_NETWORK_INVALID_PROFILE_SETTINGS";
case eNetworkAccessCode::Access_Denied_PrologueIncomplete: return "ACCESS_DENIED_NETWORK_PROLOGUE_INCOMPLETE";
case eNetworkAccessCode::Access_Denied_MultiplayerDisabled: return "ACCESS_DENIED_MULTIPLAYER_DISABLED";
case eNetworkAccessCode::Access_Denied_NotSignedIn: return "ACCESS_DENIED_NOT_SIGNED_IN";
case eNetworkAccessCode::Access_Denied_NoNetworkAccess: return "ACCESS_DENIED_NO_NETWORK_ACCESS";
case eNetworkAccessCode::Access_Denied_NotSignedOnline: return "ACCESS_DENIED_NOT_SIGNED_ONLINE";
case eNetworkAccessCode::Access_Denied_NoRosCredentials: return "ACCESS_DENIED_NO_SCS_CREDENTIALS";
case eNetworkAccessCode::Access_Denied_NoRosPrivilege: return "ACCESS_DENIED_NO_SCS_CREDENTIALS"; // "ACCESS_DENIED_NO_SCS_PRIVILEGE";
case eNetworkAccessCode::Access_Denied_NoOnlinePrivilege: return "ACCESS_DENIED_NO_ONLINE_PRIVILEGE";
case eNetworkAccessCode::Access_Denied_NoTunables: return "ACCESS_DENIED_NO_TUNABLES";
case eNetworkAccessCode::Access_Denied_NoBackgroundScript: return "ACCESS_DENIED_NO_BACKGROUND_SCRIPT";
//case eNetworkAccessCode::Access_Denied_PoliciesNotAccepted: return "ACCESS_DENIED_POLICIES_NOT_ACCEPTED";
//case eNetworkAccessCode::Access_Denied_PoliciesDownloadFailed: return "ACCESS_DENIED_POLICIES_DOWNLOAD_FAILED";
//case eNetworkAccessCode::Access_Denied_PrologueIncomplete: return "ACCESS_DENIED_PROLOGUE_INCOMPLETE";`
case eNetworkAccessCode::Access_Denied_RosBanned: return "ACCESS_DENIED_BANNED";
case eNetworkAccessCode::Access_Denied_RosSuspended: return "ACCESS_DENIED_SUSPENDED";
//case eNetworkAccessCode::Access_Denied_InvalidSandbox: return "ACCESS_DENIED_INVALID_SANDBOX";
//case eNetworkAccessCode::Access_Denied_NoXstsToken: return "ACCESS_DENIED_NO_XSTS_TOKEN";
//case eNetworkAccessCode::Access_Denied_NetworkManifestError: return "ACCESS_DENIED_NETWORK_MANIFEST_ERROR";
//case eNetworkAccessCode::Access_Denied_AgeRestricted: return "ACCESS_DENIED_AGE_RESTRICTED";
//case eNetworkAccessCode::Access_Denied_GameUpdateRequired: return "ACCESS_DENIED_GAME_UPDATE_REQUIRED";
//case eNetworkAccessCode::Access_Denied_SystemUpdateRequired: return "ACCESS_DENIED_SYSTEM_UPDATE_REQUIRED";
//case eNetworkAccessCode::Access_Denied_ServiceUnreachable: return "ACCESS_DENIED_SERVICE_UNREACHABLE";
//case eNetworkAccessCode::Access_Denied_NoRelayServerCxn: return "ACCESS_DENIED_NO_RELAY_SERVER_CXN";
//case eNetworkAccessCode::Access_Denied_IncompatibleCommandLine: return "ACCESS_DENIED_INCOMPATIBLE_COMMAND_LINE";
//case eNetworkAccessCode::Access_Denied_IncompatibleHostsFile: return "ACCESS_DENIED_INCOMPATIBLE_HOSTS_FILE";
//case eNetworkAccessCode::Access_Denied_RateLimitCloudConfigFailed: return "ACCESS_DENIED_RATE_LIMIT_CLOUD_CONFIG_FAILED";
// deprecated - fall out to default return value
case eNetworkAccessCode::Access_Deprecated_TunableNotFound:
case eNetworkAccessCode::Access_Deprecated_TunableFalse:
break;
}
return nullptr;
}
bool CNetwork::GetNetworkAccessErrorCode(const eNetworkAccessCode UNUSED_PARAM(nAccessCode), int& UNUSED_PARAM(nErrorCode))
{
// for now, just use a base string
return false;
}
bool CNetwork::GetBailExpectsErrorCode(const eBailReason UNUSED_PARAM(nReason))
{
// for now, just use a base string
return false;
}
enum BailPriority
{
Bail_HighestPriority,
Bail_HighPriority,
Bail_NormalPriority,
Bail_LowPriority,
};
BailPriority GetBailPriority(const eBailReason UNUSED_PARAM(nReason))
{
#if RSG_PC
#define PROFILE_CHANGE_PRIORITY Bail_HighestPriority
#else
#define PROFILE_CHANGE_PRIORITY Bail_HighPriority
#endif
// all the same for now
return Bail_NormalPriority;
}
void CNetwork::ShowBailWarningScreen()
{
// prevent multiple claims on the bail warning screen (or stale claims)
if(!sm_PendingBailWarningScreen)
{
gnetDebug1("ShowBailWarningScreen :: No outstanding claim");
return;
}
sm_PendingBailWarningScreen = false;
// if the incoming bail reason is lesser or equal priority to an existing alert
if(s_NetworkAlertParams.HasActiveMessage() && s_NetworkAlertParams.m_ErrorDetails.m_AlertSource == AlertScreenSource::Source_CodeBail)
{
if(GetBailPriority(sm_BailParams.m_BailReason) <= GetBailPriority(static_cast<eBailReason>(s_NetworkAlertParams.m_ErrorDetails.m_AlertContext)))
{
gnetDebug1("ShowBailWarningScreen :: Existing alert of equal or higher priority. Skipping alert.");
return;
}
}
s_NetworkAlertParams.m_ErrorDetails.m_ErrorMsg = NetworkUtils::GetBailReasonAsString(sm_BailParams.m_BailReason);
s_NetworkAlertParams.m_ErrorDetails.m_ErrorCode = sm_BailParams.m_ErrorCode;
s_NetworkAlertParams.m_ErrorDetails.m_EndPosixTime = sm_BailParams.m_EndPosixTime;
s_NetworkAlertParams.m_ErrorDetails.m_HasErrorCode = GetBailExpectsErrorCode(sm_BailParams.m_BailReason);
s_NetworkAlertParams.m_ErrorDetails.m_AlertSource = AlertScreenSource::Source_CodeBail;
s_NetworkAlertParams.m_ErrorDetails.m_AlertContext = static_cast<u32>(sm_BailParams.m_BailReason);
s_NetworkAlertParams.m_ResolveRequiresOnline = false;
#if RSG_OUTPUT
s_NetworkAlertParams.m_ErrorDetails.m_DeveloperMode = !NetworkInterface::IsUsingCompliance();
#endif
// setup update once to show the screen
SetupNetworkAlertScreen();
UpdateNetworkAlertScreen();
}
bool CNetwork::Bail(const sBailParameters bailParams, bool bSendScriptEvent)
{
#if !__NO_OUTPUT
// log callstack
sysStack::PrintStackTrace();
#endif
// logging
gnetDebug1("Network::Bail :: %s, Code: 0x%08x, Error: %s, Context: %d / %d / %d, EndTime: %" I64FMT "d",
NetworkUtils::GetBailReasonAsString(bailParams.m_BailReason),
bailParams.m_ErrorCode,
bailParams.m_ErrorString ? bailParams.m_ErrorString : "None",
bailParams.m_ContextParam1,
bailParams.m_ContextParam2,
bailParams.m_ContextParam3,
bailParams.m_EndPosixTime);
// check if we have an equal or higher priority bail reason pending
if(sm_IsBailPending && (GetBailPriority(sm_BailParams.m_BailReason) < GetBailPriority(bailParams.m_BailReason)))
{
gnetDebug1("Network::Bail :: Ignoring, higher priority pending bail: %s", NetworkUtils::GetBailReasonAsString(sm_BailParams.m_BailReason));
return false;
}
// action bails in our update loop
sm_IsBailPending = true;
// capture parameters
sm_AlertScriptForBail = bSendScriptEvent;
sm_BailParams = bailParams;
return true;
}
void CNetwork::BailImmediate(const sBailParameters& p)
{
gnetDebug1("Network::BailImmediate :: Bailing");
if(Bail(p))
{
gnetDebug1("Network::BailImmediate :: Actioning");
ActionBail();
}
}
void CNetwork::ActionBail()
{
gnetDebug1("Network::ActionBail :: %s, Code: 0x%08x, Error: %s, Context: %d / %d / %d, EndTime: %" I64FMT "d",
NetworkUtils::GetBailReasonAsString(sm_BailParams.m_BailReason),
sm_BailParams.m_ErrorCode,
sm_BailParams.m_ErrorString ? sm_BailParams.m_ErrorString : "None",
sm_BailParams.m_ContextParam1,
sm_BailParams.m_ContextParam2,
sm_BailParams.m_ContextParam3,
sm_BailParams.m_EndPosixTime);
// reset pending flag
sm_IsBailPending = true;
// the bail event (if requested) fires regardless of the network being open - we might trigger a bail during matchmaking
if(sm_AlertScriptForBail)
{
gnetDebug1("Network::ActionBail :: Alerting script");
GetEventScriptNetworkGroup()->Add(CEventNetworkBail(sm_BailParams.m_BailReason, sm_BailParams.m_ErrorCode));
}
// check if we should handle the bail alerting in code
if(s_EnableCodeBailAlerting)
{
gnetDebug1("Network::ActionBail :: Handling bail warning screen");
CNetwork::ShowBailWarningScreen();
}
// shutdown network session ahead of closing the managers - flag this as a sudden shutdown
if(sm_NetworkSession)
{
sm_NetworkSession->OnBail(sm_BailParams);
sm_NetworkSession->Shutdown(true, sm_BailParams.m_BailReason);
}
// only close if we are currently open
CNetwork::CloseNetwork(true);
// at the least, reset these
sm_bFirstEntryNetworkOpen = false;
sm_bFirstEntryMatchStarted = false;
if(sm_bSkipRadioResetNextClose)
{
audDisplayf("CNetwork::ActionBail :: Resetting sm_bSkipRadioResetNextClose");
sm_bSkipRadioResetNextClose = false;
}
// Also reset the 'skip reset on next open' flag
sm_bSkipRadioResetNextOpen = false;
// reinitialize network session
if(sm_NetworkSession)
{
gnetVerifyf(sm_NetworkSession->Init(), "CNetwork::ActionBail - Failed to initialise NetworkSession");
}
// if we are bailing BAIL_SYSTEM_SUSPENDED - we need to make sure we cancel any
// pending saves or saves in progress and that we block save requests.
// Reference - url:bugstar:6007512
if(eBailReason::BAIL_SYSTEM_SUSPENDED == sm_BailParams.m_BailReason)
{
CStatsMgr::GetStatsDataMgr().GetSavesMgr().SetBlockSaveRequests(true);
CStatsMgr::GetStatsDataMgr().GetSavesMgr().CancelAndClearSaveRequests();
}
}
bool DoesDateHaveMonthDelimiter(const sysLanguage language)
{
switch(language)
{
// the localization entries for these languages already include the month delimiter
// instead of adding new entries or potentially breaking other uses of those labels,
// override the delimiter setting here
case LANGUAGE_KOREAN:
case LANGUAGE_CHINESE_TRADITIONAL:
case LANGUAGE_CHINESE_SIMPLIFIED:
return false;
default:
return fwLanguagePack::HasMonthDelimiter(language);
}
}
void CNetwork::FormatPosixTimeAsUntilDate(const s64 posixTime, char* dateString, const unsigned maxLength)
{
if(dateString == nullptr)
return;
time_t theTime = posixTime;
tm tmTime = *gmtime(&theTime);
const sysLanguage language = g_SysService.GetSystemLanguage();
LocalizationKey monthLngKey;
fwLanguagePack::GetMonthString(monthLngKey.getBufferRef(), tmTime.tm_mon + 1, false, fwLanguagePack::NeedsLowerCaseMonth(language));
LocalizationKey dayLngKey;
fwLanguagePack::GetDayOfTheMonthString(dayLngKey.getBufferRef(), tmTime.tm_mday);
const fwLanguagePack::DATE_FORMAT dateFormat = fwLanguagePack::GetDateFormatType(language);
static const unsigned MAX_TOKEN_LENGTH = 64;
char szDay[MAX_TOKEN_LENGTH], szMonth[MAX_TOKEN_LENGTH], szYear[MAX_TOKEN_LENGTH];
formatf(szDay, "%s", TheText.Get(dayLngKey.getBufferRef()));
formatf(szMonth, "%s", TheText.Get(monthLngKey.getBufferRef()));
formatf(szYear, "%u", tmTime.tm_year + 1900);
// delimiters - include a tunable to remove these
const bool allowDelimiters = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NETWORK_DATE_ALLOW_DELIMITERS", 0xf90a681d), true);
const bool hasDayDelimiter = allowDelimiters && fwLanguagePack::HasDayDelimiter(language);
const bool hasMonthDelimiter = allowDelimiters && DoesDateHaveMonthDelimiter(language);
const bool hasYearDelimiter = allowDelimiters && fwLanguagePack::HasYearDelimiter(language);
// spacing is embedded in the delimiter strings
static const unsigned MAX_DELIM_LENGTH = 8;
char szDayDelim[MAX_DELIM_LENGTH], szMonthDelim[MAX_DELIM_LENGTH], szYearDelim[MAX_DELIM_LENGTH], szPreDelim[MAX_DELIM_LENGTH], szPostDelim[MAX_DELIM_LENGTH];
formatf(szDayDelim, "%s", hasDayDelimiter ? TheText.Get("DATE_DAY_DELIM") : " ");
formatf(szMonthDelim, "%s", hasMonthDelimiter ? TheText.Get("DATE_MONTH_DELIM") : " ");
formatf(szYearDelim, "%s", hasYearDelimiter ? TheText.Get("DATE_YEAR_DELIM") : " ");
// work out whether we pre/post-fix delimiters with spaces or not
formatf(szPreDelim, "%s", fwLanguagePack::DoesPrefixDelimiterWithSpace(language) ? " " : "");
formatf(szPostDelim, "%s", fwLanguagePack::DoesPostfixDelimiterWithSpace(language) ? " " : "");
// use an atString to make this easier
atString date;
// need three passes - add the correct delimiter for each case
switch(dateFormat)
{
case fwLanguagePack::DATE_FORMAT_DMY: date += szDay; date += hasDayDelimiter ? szPreDelim : ""; date += szDayDelim; date += hasDayDelimiter ? szPostDelim : ""; break;
case fwLanguagePack::DATE_FORMAT_MDY: date += szMonth; date += hasMonthDelimiter ? szPreDelim : ""; date += szMonthDelim; date += hasMonthDelimiter ? szPostDelim : ""; break;
case fwLanguagePack::DATE_FORMAT_YMD: date += szYear; date += hasYearDelimiter ? szPreDelim : ""; date += szYearDelim; date += hasYearDelimiter ? szPostDelim : ""; break;
default: break;
}
switch(dateFormat)
{
case fwLanguagePack::DATE_FORMAT_DMY: date += szMonth; date += hasMonthDelimiter ? szPreDelim : ""; date += szMonthDelim; date += hasMonthDelimiter ? szPostDelim : ""; break;
case fwLanguagePack::DATE_FORMAT_MDY: date += szDay; date += hasDayDelimiter ? szPreDelim : ""; date += szDayDelim; date += hasDayDelimiter ? szPostDelim : ""; break;
case fwLanguagePack::DATE_FORMAT_YMD: date += szMonth; date += hasMonthDelimiter ? szPreDelim : ""; date += szMonthDelim; date += hasMonthDelimiter ? szPostDelim : ""; break;
default: break;
}
// don't add a space for the last token if we don't have a delimiter
switch(dateFormat)
{
case fwLanguagePack::DATE_FORMAT_DMY: date += szYear; date += hasYearDelimiter ? szPreDelim : ""; date += hasYearDelimiter ? szYearDelim : ""; /* no space after the final token */ break;
case fwLanguagePack::DATE_FORMAT_MDY: date += szYear; date += hasYearDelimiter ? szPreDelim : ""; date += hasYearDelimiter ? szYearDelim : ""; /* no space after the final token */ break;
case fwLanguagePack::DATE_FORMAT_YMD: date += szDay; date += hasDayDelimiter ? szPreDelim : ""; date += hasDayDelimiter ? szDayDelim : ""; /* no space after the final token */ break;
default: break;
}
safecpy(dateString, date.c_str(), maxLength);
}
void CNetwork::SetupNetworkAlertScreen()
{
// build the network alert sub-string
if(s_NetworkAlertParams.m_ErrorDetails.m_HasErrorCode)
{
formatf(s_NetworkAlertParams.m_ErrorDetails.m_SubString, "0x%08x", static_cast<unsigned>(s_NetworkAlertParams.m_ErrorDetails.m_ErrorCode));
s_NetworkAlertParams.m_ErrorDetails.m_HasSubString = true;
}
else if(s_NetworkAlertParams.m_ErrorDetails.HasEndTime())
{
FormatPosixTimeAsUntilDate(
s_NetworkAlertParams.m_ErrorDetails.m_EndPosixTime,
s_NetworkAlertParams.m_ErrorDetails.m_SubString,
sNetworkErrorDetails::MAX_SUBSTRING_LENGTH);
s_NetworkAlertParams.m_ErrorDetails.m_HasSubString = true;
}
else
{
s_NetworkAlertParams.m_ErrorDetails.m_HasSubString = false;
}
// close the calibration screen if open (warning screen compatibility)
CPauseMenu::CloseDisplayCalibrationScreen();
// manage existing alerts
if(s_NetworkAlertParams.HasActiveMessage())
{
// nothing to do, the warning is re-posted each frame on V
}
else
{
s_NetworkAlertParams.m_InstanceHash = static_cast<u32>(fwRandom::GetRandomNumber());
}
// set the message as active
s_NetworkAlertParams.SetActive();
#define NET_SEND_ALERT_TELEMETRY (0)
#if NET_SEND_ALERT_TELEMETRY
// send alert telemetry
NetworkMetricsCommonInterface::MultiplayerErrorAlert(
s_NetworkAlertParams.m_ErrorDetails.m_AlertSource,
s_NetworkAlertParams.m_ErrorDetails.m_AlertContext,
atStringHash(s_NetworkAlertParams.m_ErrorDetails.m_ErrorMsg),
s_NetworkAlertParams.m_ErrorDetails.m_ErrorCode,
s_NetworkAlertParams.m_InstanceHash,
CNetwork::IsNetworkErrorCodeExpected(s_NetworkAlertParams.m_ErrorDetails.m_ErrorCode),
false);
#endif
}
void
CNetwork::UpdateNetworkAlertScreen()
{
if(s_NetworkAlertParams.HasActiveMessage())
{
if(CWarningScreen::CheckAllInput() == FE_WARNING_CONTINUE)
{
CWarningScreen::Remove();
ClearNetworkAlertScreen();
}
else
{
static const char* s_Heading = "HUD_CONNPROB";
CWarningScreen::SetMessageWithHeader(
WARNING_MESSAGE_STANDARD,
s_Heading,
s_NetworkAlertParams.m_ErrorDetails.m_ErrorMsg,
FE_WARNING_CONTINUE,
false,
-1,
s_NetworkAlertParams.m_ErrorDetails.m_HasSubString ? s_NetworkAlertParams.m_ErrorDetails.m_SubString : nullptr);
}
}
}
void CNetwork::ClearNetworkAlertScreen()
{
if(s_NetworkAlertParams.HasActiveMessage())
{
gnetDebug1("ClearNetworkAlertScreen");
s_NetworkAlertParams.ClearMessage();
}
}
void CNetwork::CleanupNetworkAlertScreen()
{
gnetDebug1("CleanupNetworkAlertScreen");
ClearNetworkAlertScreen();
}
void CNetwork::OnPlatformInvitePassedChecks()
{
gnetDebug1("OnPlatformInvitePassedChecks");
ClearNetworkAlertScreen();
}
netConnectionManager&
CNetwork::GetConnectionManager()
{
return sm_CxnMgr;
}
// #if __BANK
sysMemAllocator&
CNetwork::GetSCxnMgrAllocator()
{
return *s_SCxnMgrAllocator;
}
// #endif // __BANK
void
CNetwork::SetScriptReadyForEvents(bool bReady)
{
gnetDebug1("SetScriptReadyForEvents :: %s", bReady ? "Ready" : "Not Ready");
sm_bScriptReadyForEvents = bReady;
}
void
CNetwork::SetScriptAutoMuted(bool bIsAutoMuted)
{
if(sm_bIsScriptAutoMuted != bIsAutoMuted)
{
gnetDebug1("SetScriptAutoMuted :: %s", bIsAutoMuted ? "True" : "False");
sm_bIsScriptAutoMuted = bIsAutoMuted;
}
}
bool
CNetwork::IsCheater(const int nLocalPlayerIndex)
{
#if !__FINAL
int nCheater = 0;
if(PARAM_netForceCheater.Get(nCheater))
return nCheater > 0 ? true : false;
#endif
int playerIndex = nLocalPlayerIndex >= 0 ? nLocalPlayerIndex : NetworkInterface::GetLocalGamerIndex();
if(playerIndex<0)
{
gnetWarning("CNetwork::IsCheater called with an invalid localPlayerIndex");
return false;
}
// this is the master setting
if(rlRos::HasPrivilege(playerIndex, RLROS_PRIVILEGEID_CHEATER_POOL))
return true;
// check override
if(CLiveManager::GetSCGamerDataMgr().HasCheaterOverride())
return false;
return CLiveManager::GetSCGamerDataMgr().IsCheater();
}
void
CNetwork::PlayerHasJoinedSession(const netPlayer& player)
{
if(sm_bNetworkOpen)
{
CContextMenuHelper::PlayerHasJoined(player.GetPhysicalPlayerIndex());
if (sm_bManagersAreInitialised)
{
sm_RoamingBubbleMgr.PlayerHasJoined(player);
#if ENABLE_NETWORK_BOTS
CNetworkBot::OnPlayerHasJoined(player);
#endif // ENABLE_NETWORK_BOTS
}
if (!player.IsLocal())
{
CStatsMgr::GetStatsDataMgr().PlayerHasJoinedSession(player.GetActivePlayerIndex());
}
// Update the kick votes on player that joined
if (NetworkInterface::IsGameInProgress() && player.IsRemote() && !player.IsBot())
{
const PlayerFlags votes = netPlayer::GetLocalPlayerKickVotes();
if (votes)
{
CSendKickVotesEvent::Trigger(votes, 1<<player.GetPhysicalPlayerIndex());
}
if (NetworkInterface::IsHost())
{
#if !__FINAL
// sync modification to clock
if(CClock::HasDebugModifiedInNetworkGame())
CDebugGameClockEvent::Trigger(CDebugGameClockEvent::FLAG_IS_DEBUG_CHANGE, &player);
else
CDebugGameClockEvent::Trigger(CDebugGameClockEvent::FLAG_IS_DEBUG_RESET, &player);
// sync forced weather
if(g_weather.GetIsForced() && g_weather.GetNetSendForceUpdate())
CDebugGameWeatherEvent::Trigger(g_weather.GetIsForced(), g_weather.GetPrevTypeIndex(), g_weather.GetCycleIndex(), &player);
#endif
}
}
}
}
void
CNetwork::PlayerHasLeftSession(const netPlayer& player)
{
if(sm_bNetworkOpen)
{
gnetAssert(!player.IsMyPlayer());
CStatsMgr::GetStatsDataMgr().PlayerHasLeftSession(player.GetActivePlayerIndex());
CContextMenuHelper::PlayerHasLeft(player.GetPhysicalPlayerIndex());
if(SPlayerCardManager::IsInstantiated())
{
CPlayerCardManager& cardMgr = SPlayerCardManager::GetInstance();
CActivePlayerCardDataManager* pCard = static_cast<CActivePlayerCardDataManager*>(cardMgr.GetDataManager(CPlayerCardManager::CARDTYPE_ACTIVE_PLAYER));
if(pCard && pCard->IsValid())
{
pCard->FlagForRefresh();
}
}
// inform pickup manager player has left
if (player.GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX)
{
CPickupManager::RemovePlayerFromAllProhibitedCollections(player.GetPhysicalPlayerIndex());
}
if (player.IsRemote() && !player.IsBot())
{
netPlayer::RemoveLocalKickVote(player);
// Remove this player's kick votes for other players
unsigned numActivePlayers = netInterface::GetNumActivePlayers();
const netPlayer * const *activePlayers = netInterface::GetAllActivePlayers();
for(unsigned index = 0; index < numActivePlayers; index++)
{
netPlayer *pPlayer = (netPlayer *)activePlayers[index];
pPlayer->RemoveKickVote(player);
}
}
if (sm_bManagersAreInitialised)
{
sm_RoamingBubbleMgr.PlayerHasLeft(player);
#if ENABLE_NETWORK_BOTS
CNetworkBot::OnPlayerHasLeft(player);
#endif // ENABLE_NETWORK_BOTS
}
}
}
void
CNetwork::PlayerHasJoinedBubble(const netPlayer& player)
{
// the player must have a valid physical player index at this point
gnetAssert(player.GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX);
if(sm_bNetworkOpen && sm_bManagersAreInitialised)
{
sm_BandwidthMgr.PlayerHasJoined(player);
NetworkDebug::PlayerHasJoined(player);
if (player.IsMyPlayer())
{
// TODO: when the local player joins a new bubble, the managers may have to be informed
PhysicalPlayerIndex playerIndex = player.GetPhysicalPlayerIndex();
if(gnetVerifyf(playerIndex != INVALID_PLAYER_INDEX, "Adding local player to a bubble with an invalid physical player index"))
{
if(sm_ObjectMgr)
{
sm_ObjectMgr->GetObjectIDManager().SetObjectIdRangeIndex(playerIndex);
}
}
sm_bGameInProgress = sm_bMatchStarted;
}
else
{
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetMessageLog(), "PLAYER_HAS_JOINED", "%s", player.GetLogName());
if(sm_EventMgr)
{
sm_EventMgr->PlayerHasJoined(player); // event manager has to be called first as other managers may trigger network events
}
if(sm_ObjectMgr)
{
sm_ObjectMgr->PlayerHasJoined(player);
}
if(sm_ArrayMgr)
{
sm_ArrayMgr->PlayerHasJoined(player);
}
// also inform the script handler manager (the script handlers have network components). This could be done better with a system event.
CTheScripts::GetScriptHandlerMgr().PlayerHasJoined(player);
//Inform telemtry player has joined
CNetworkTelemetry::PlayerHasJoined(player);
}
// inform other systems that are interested
CGarages::PlayerHasJoined(player.GetPhysicalPlayerIndex());
// reset flags that use physical player index
sm_NetworkVoice.PlayerHasJoinedBubble(player.GetGamerInfo());
}
}
//Grabs the player ped and set a bogus ped to be cleared during player removal.
//Must be done before the player network object is removed since the playerped is cleared during that removal.
static CPed* LeavePedBehindForPlayerLeavingSession(CPed* ped, ObjectId& objId, const netPlayer& leavingPlayer)
{
NetworkLogUtils::WritePlayerText(NetworkInterface::GetObjectManager().GetLog(), leavingPlayer.GetPhysicalPlayerIndex(), "LEAVE_PED_BEHIND_FOR_LEAVING_PLAYER", "");
gnetDebug1("LeavePedBehindForPlayerLeavingSession() - %s", leavingPlayer.GetLogName());
respawnDebugf3("LeavePedBehindForPlayerLeavingSession() - %s", leavingPlayer.GetLogName());
objId = NETWORK_INVALID_OBJECT_ID;
ped = 0;
if (!NetworkInterface::IsGameInProgress())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !NetworkInterface::IsGameInProgress()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !NetworkInterface::IsGameInProgress()");
return 0;
}
CPed* localPlayerPed = FindPlayerPed();
if(!localPlayerPed)
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed");
return 0;
}
if(!localPlayerPed->GetNetworkObject())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed->GetNetworkObject()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed->GetNetworkObject()");
return 0;
}
if(!localPlayerPed->GetNetworkObject()->GetPlayerOwner())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed->GetNetworkObject()->GetPlayerOwner()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed->GetNetworkObject()->GetPlayerOwner()");
return 0;
}
if(!localPlayerPed->GetNetworkObject()->GetPlayerOwner()->IsPhysical())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed->GetNetworkObject()->GetPlayerOwner()->IsPhysical()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !localPlayerPed->GetNetworkObject()->GetPlayerOwner()->IsPhysical()");
return 0;
}
if (!leavingPlayer.IsPhysical())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !leavingPlayer.IsPhysical()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !leavingPlayer.IsPhysical()");
return 0;
}
if (!leavingPlayer.IsRemote())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !leavingPlayer.IsRemote()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !leavingPlayer.IsRemote()");
return 0;
}
if (leavingPlayer.IsBot())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - leavingPlayer.IsBot()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - leavingPlayer.IsBot()");
return 0;
}
if (PARAM_disableLeavePed.Get())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - PARAM_disableLeavePed.Get()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - PARAM_disableLeavePed.Get()");
return 0;
}
ped = ((CNetGamePlayer*)(&leavingPlayer))->GetPlayerPed();
// stop any synced scene from running on the leaving player ped
if(ped && ped->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_SYNCHRONIZED_SCENE))
{
ped->GetPedIntelligence()->FlushImmediately(true);
}
//If they aren't visible then don't leave a ped behind even to make then disappear later.
//Likely script or fading out left them invisible. If it was script and in the apartment need to isolate there rather than here.
respawnDebugf3("Note: (ped[%p] && ped->GetNetworkObject()[%p] && ped->IsVisible()[%d]) localPlayerPed->GetPlayerInfo()->GetPlayerLeavePedBehind()[%d]",ped,ped ? ped->GetNetworkObject() : nullptr,ped ? ped->IsVisible() : false,localPlayerPed->GetPlayerInfo() ? localPlayerPed->GetPlayerInfo()->GetPlayerLeavePedBehind() : false);
if (ped && ped->GetNetworkObject() && ped->IsVisible())
{
//Leave ped behind has been disabled.
if (static_cast< CNetObjPlayer* >( ped->GetNetworkObject() )->GetDisableLeavePedBehind())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - GetDisableLeavePedBehind()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - GetDisableLeavePedBehind()");
return 0;
}
respawnDebugf3("(ped && ped->GetNetworkObject() && ped->IsVisible())");
objId = ped->GetNetworkObject()->GetObjectID();
const CControlledByInfo networkNpcControl(true, false);
Matrix34 tempMat;
tempMat.Identity();
tempMat.d = VEC3V_TO_VECTOR3( ped->GetTransform().GetPosition() );
CPed* deletePed = CPedFactory::GetFactory()->CreatePed(networkNpcControl, ped->GetModelId(), &tempMat, true, false, false);
if (gnetVerify( deletePed ))
{
respawnDebugf3("(deletePed)");
u32 defaultRelationshipGroupHash = ped->GetPedIntelligence()->GetRelationshipGroupDefault()->GetName().GetHash();
u32 relationshipGroupHash = ped->GetPedIntelligence()->GetRelationshipGroup()->GetName().GetHash();
//This can distort the relationship groups with the call to SetDefaultRelationshipGroup - use the above cached values and set them for the ped after ChangePlayerPed is invoked.
CGameWorld::ChangePlayerPed(*ped, *deletePed);
//Add the delete ped to the scene update - this ped gets flagged for deletion in the same frame and
//won't get cleaned up if we don't do this
deletePed->AddToSceneUpdate();
//Establish the proper relationship group for the replacement peds
CRelationshipGroup* pGroup = CRelationshipManager::FindRelationshipGroup(defaultRelationshipGroupHash);
if( pGroup )
{
ped->GetPedIntelligence()->SetRelationshipGroupDefault(pGroup);
deletePed->GetPedIntelligence()->SetRelationshipGroupDefault(pGroup);
}
pGroup = CRelationshipManager::FindRelationshipGroup(relationshipGroupHash);
if( pGroup )
{
ped->GetPedIntelligence()->SetRelationshipGroup(pGroup);
deletePed->GetPedIntelligence()->SetRelationshipGroup(pGroup);
}
//Remove all weapons except unarmed weapons
ped->GetInventory()->RemoveAllWeaponsExcept( ped->GetDefaultUnarmedWeaponHash() );
deletePed->GetInventory()->RemoveAllWeaponsExcept( deletePed->GetDefaultUnarmedWeaponHash() );
//Ensure that the ped will not fight if unarmed
ped->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_CanFightArmedPedsWhenNotArmed);
deletePed->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_CanFightArmedPedsWhenNotArmed);
ped->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_Aggressive);
deletePed->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_Aggressive);
ped->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_AlwaysFight);
deletePed->GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_AlwaysFight);
//Ensure the ped isn't freaked out by shocking events
ped->SetPedConfigFlag(CPED_CONFIG_FLAG_DisableShockingEvents, true);
deletePed->SetPedConfigFlag(CPED_CONFIG_FLAG_DisableShockingEvents, true);
//Adjust health so that the created ped will have less health and die more easily
ped->SetMaxHealth(200.f);
deletePed->SetMaxHealth(200.f);
if (ped->GetHealth() > 150.f)
ped->SetHealth(150.f, true);
if (deletePed->GetHealth() > 150.f)
deletePed->SetHealth(150.f, true);
ped->m_nPhysicalFlags.bNotDamagedByAnything = false; //ensure this is reset
if(!localPlayerPed->GetPlayerInfo()->GetPlayerLeavePedBehind())
{
//This will enable special alpha ramping for the replacement ped then after an interval the ped will be removed from the world
ped->SetSpecialNetworkLeave();
}
else
{
ped->SetSpecialNetworkLeaveWhenDead();
}
if (ped->GetMyVehicle() && ped->GetVehiclePedInside())
{
respawnDebugf3("(ped->GetMyVehicle() && ped->GetVehiclePedInside())");
CVehicle* pVehicle = ped->GetMyVehicle();
if (pVehicle->GetIsAircraft())
{
//Give a parachute
ped->GetInventory()->AddWeapon(GADGETTYPE_PARACHUTE);
deletePed->GetInventory()->AddWeapon(GADGETTYPE_PARACHUTE);
}
//By default place the previous player out of the vehicle, the newplayerped is what should remain as the previous player ped is being removed.
//This will have the ped get out of the vehicle and allow the co-driver to take control. If the ped is in rear seats they will just get out.
//If they don't get out they tend to aggress the driver.
bool bFixupPedVehicleState = true;
//override - if the vehicle is scripted then the replacement ped should always get out.
bool bScriptedVehicleOverride = pVehicle->GetNetworkObject() ? pVehicle->GetNetworkObject()->IsGlobalFlagSet(CNetObjGame::GLOBALFLAG_SCRIPTOBJECT) : false;
if (!bScriptedVehicleOverride && pVehicle->IsDriver(ped))
{
CPed* pSeat1 = pVehicle->GetSeatManager()->GetPedInSeat(1);
if (!pSeat1 || !pSeat1->IsPlayer())
{
//If the player ped is in a vehicle alone, or in a vehicle with AI peds in the co-driver seat. Then let them remain in the seat and continue driving as a new AI ped.
bFixupPedVehicleState = false;
//clear out the rear brake as well
if (pVehicle->InheritsFromBicycle())
{
CBike* pVehAsBike = static_cast<CBike*>(pVehicle);
pVehAsBike->SetRearBrake(0.0f);
}
}
}
if (bFixupPedVehicleState)
{
respawnDebugf3("(bFixupPedVehicleState)");
//Ensure on change of ownership the ped will try to exit the vehicle
ped->SetExitVehicleOnChangeOwner(true);
deletePed->SetExitVehicleOnChangeOwner(true);
//Ensure the ped will flee when he leaves the vehicle
ped->GetPedIntelligence()->GetCombatBehaviour().SetFlag(CCombatData::BF_AlwaysFlee);
deletePed->GetPedIntelligence()->GetCombatBehaviour().SetFlag(CCombatData::BF_AlwaysFlee);
//Ensure the old ped tasks are cleared out and started out of the vehicle
ped->GetPedIntelligence()->ClearPrimaryTask();
ped->GetPedIntelligence()->ClearTaskEventResponse();
ped->GetPedIntelligence()->AddTaskDefault(ped->ComputeDefaultTask(*ped));
//Ensure the replacement ped tasks are cleared out and started out of the vehicle
deletePed->GetPedIntelligence()->ClearPrimaryTask();
deletePed->GetPedIntelligence()->ClearTaskEventResponse();
deletePed->GetPedIntelligence()->AddTaskDefault(deletePed->ComputeDefaultTask(*deletePed));
}
}
else
{
respawnDebugf3("ped is not in a vehicle -- SetMyVehicle(NULL) -- so he doesn't get back into a vehicle he previously was in");
ped->SetMyVehicle(NULL); //ensure this is cleared out if the ped isn't in a vehicle
deletePed->SetMyVehicle(NULL);
// make sure the ped has collision
ped->EnableCollision();
}
if (gnetVerify(ped != ((CNetGamePlayer*)(&leavingPlayer))->GetPlayerPed()))
{
respawnDebugf3("ped != ((CNetGamePlayer*)(&leavingPlayer))->GetPlayerPed()");
if (ped->IsBaseFlagSet(fwEntity::IS_FIXED))
{
respawnDebugf3("ped->IsBaseFlagSet(fwEntity::IS_FIXED)-->ped->SetFixedPhysics(false, false)");
ped->SetFixedPhysics(false, false);
}
respawnDebugf3("return ped");
return ped;
}
else if(gnetVerify(deletePed) && gnetVerify(ped != deletePed))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - CGameWorld::ChangePlayerPed()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - CGameWorld::ChangePlayerPed()");
CPedFactory::GetFactory()->DestroyPed(deletePed);
}
}
else
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - CPedFactory::GetFactory()->CreatePed()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - CPedFactory::GetFactory()->CreatePed()");
}
}
else if(!ped)
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped");
}
else if(!ped->GetNetworkObject())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->GetNetworkObject()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->GetNetworkObject()");
}
else if(!ped->IsVisible())
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible()");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible()");
#if !__NO_OUTPUT
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_DEBUG ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_DEBUG");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_DEBUG");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_CAMERA ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_CAMERA");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_CAMERA");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_SCRIPT ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_SCRIPT");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_SCRIPT");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_CUTSCENE ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_CUTSCENE");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_CUTSCENE");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_GAMEPLAY ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_GAMEPLAY");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_GAMEPLAY");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_FRONTEND ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_FRONTEND");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_FRONTEND");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_VFX ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_VFX");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_VFX");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_NETWORK ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_NETWORK");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_NETWORK");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_PHYSICS ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_PHYSICS");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_PHYSICS");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_WORLD ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_WORLD");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_WORLD");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_DUMMY_CONVERSION ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_DUMMY_CONVERSION");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_DUMMY_CONVERSION");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_PLAYER ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_PLAYER");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_PLAYER");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_PICKUP ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_PICKUP");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_PICKUP");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_FIRST_PERSON ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_FIRST_PERSON");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_FIRST_PERSON");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_VEHICLE_RESPOT ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_VEHICLE_RESPOT");
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_VEHICLE_RESPOT");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_RESPAWN ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_RESPAWN");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_RESPAWN");
}
if (!ped->GetIsVisibleForModule( SETISVISIBLE_MODULE_REPLAY ))
{
gnetDebug1("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_REPLAY");
respawnDebugf3("FAIL LeavePedBehindForPlayerLeavingSession() - !ped->IsVisible() - SETISVISIBLE_MODULE_REPLAY");
}
#endif //
}
objId = NETWORK_INVALID_OBJECT_ID;
ped = 0;
return 0;
}
//Creates a new network object using the player ID and sets that network object to use the ped that was formelly with the player leaving.
//Must be done after the player network object is UnRegistered.
static void CreateClonePedForPlayerLeavingSession(CPed* gameObject, const ObjectId objId, const netPlayer& leavingPlayer)
{
if (!gameObject)
return;
bool result = false;
//create a ped network object and set to have this gameobject. add it to the reassign list.
if (gameObject && objId != NETWORK_INVALID_OBJECT_ID)
{
if (!CNetworkObjectPopulationMgr::CanRegisterObject(NET_OBJ_TYPE_PED, true))
{
NetworkInterface::GetObjectManager().TryToMakeSpaceForObject(NET_OBJ_TYPE_PED);
}
if (CNetworkObjectPopulationMgr::CanRegisterObject(NET_OBJ_TYPE_PED, true))
{
netObject* netObj = CPed::StaticCreateNetworkObject(objId, leavingPlayer.GetPhysicalPlayerIndex(), 0, MAX_NUM_PHYSICAL_PLAYERS);
if (gnetVerify(netObj))
{
netObj->SetGameObject( gameObject );
gameObject->SetNetworkObject( netObj );
//Important: ensure that when ChangeOwner is called for the CNetObjPed after the ownership is established the remote clone will process as if it was previously a local then transitioned to clone.
//This must happen otherwise the remote will not properly be setup and will not properly be in or out of a vehicle. lavalley.
CNetObjPed* pNetObjPed = static_cast<CNetObjPed*>(netObj);
if (pNetObjPed)
pNetObjPed->SetForceCloneTransitionChangeOwner();
NetworkInterface::GetObjectManager().RegisterNetworkObject(netObj);
CNetwork::GetObjectManager().GetReassignMgr().AddObjectToReassignList(*netObj);
//Add network event informing the ped id.
CEventNetworkPedLeftBehind scrEvent(gameObject, CEventNetworkPedLeftBehind::REASON_PLAYER_LEFT_SESSION);
GetEventScriptNetworkGroup()->Add(scrEvent);
result = true;
}
}
}
//If it fails destroy the GameObject.
if (!result)
{
//Ensure the count is properly updated.
CPedPopulation::RemovePedFromPopulationCount(gameObject);
CPedFactory::GetFactory()->DestroyPlayerPed(gameObject);
}
}
void
CNetwork::PlayerHasLeftBubble(const netPlayer& player)
{
// the player must still have a valid physical player index at this point
gnetAssert(player.GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX);
if(sm_bNetworkOpen && sm_bManagersAreInitialised)
{
sm_BandwidthMgr.PlayerHasLeft(player);
if (player.IsMyPlayer())
{
// TODO: when the local player leaves his current bubble, the managers will have to be informed and probably have to do some cleanup
sm_bGameInProgress = false;
}
else
{
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetMessageLog(), "PLAYER_HAS_LEFT", "%s", player.GetLogName());
// inform the script handler manager (the script handlers have network components). This could be done better with a system event.
CTheScripts::GetScriptHandlerMgr().PlayerHasLeft(player);
if(sm_ObjectMgr)
{
//Leave a ped behind so that this player does not disappear.
ObjectId objId = NETWORK_INVALID_OBJECT_ID;
CPed* ped = 0;
ped = LeavePedBehindForPlayerLeavingSession(ped, objId, player);
sm_ObjectMgr->PlayerHasLeft(player);
//create a ped network object and set to have this ped. add it to the reassign list.
CreateClonePedForPlayerLeavingSession(ped, objId, player);
}
if(sm_EventMgr)
{
sm_EventMgr->PlayerHasLeft(player);
}
if(sm_ArrayMgr)
{
sm_ArrayMgr->PlayerHasLeft(player);
}
// inform pickup manager player has left
if (player.GetPhysicalPlayerIndex() != INVALID_PLAYER_INDEX)
{
CPickupManager::RemovePlayerFromAllProhibitedCollections(player.GetPhysicalPlayerIndex());
}
}
// inform other systems that are interested
CGarages::PlayerHasLeft(player.GetPhysicalPlayerIndex());
// reset flags that use physical player index
sm_NetworkVoice.PlayerHasLeftBubble(player.GetGamerInfo());
}
}
bool
CNetwork::IsConnectionValid(const int connectionId)
{
return sm_CxnMgr.IsOpen(connectionId)
&& NetworkInterface::GetPlayerFromConnectionId(connectionId);
}
netLoggingInterface&
CNetwork::GetMessageLog()
{
return *s_MessageLog;
}
#if ENABLE_NETWORK_LOGGING
void CNetwork::OnAssertionFired()
{
s_AssertionFiredLastFrame = true;
}
#endif // ENABLE_NETWORK_LOGGING
void CNetwork::FlushAllLogFiles(bool waitForFlush)
{
static bool flushingLogFiles = false;
if(!flushingLogFiles)
{
flushingLogFiles = true;
if(sm_bManagersAreInitialised)
{
if(sm_ArrayMgr)
{
sm_ArrayMgr->GetLog().Flush(waitForFlush);
}
if(sm_EventMgr)
{
sm_EventMgr->GetLog().Flush(waitForFlush);
}
if(sm_ObjectMgr)
{
sm_ObjectMgr->GetLog().Flush(waitForFlush);
sm_ObjectMgr->GetReassignMgr().GetLog().Flush(waitForFlush);
}
sm_PlayerMgr.GetLog().Flush(waitForFlush);
sm_BandwidthMgr.GetLog().Flush(waitForFlush);
}
BANK_ONLY(NetworkDebug::FlushDebugLogs(waitForFlush));
if(s_MessageLog)
{
s_MessageLog->Flush(waitForFlush);
}
if(CTheScripts::GetScriptHandlerMgr().GetLog())
{
CTheScripts::GetScriptHandlerMgr().GetLog()->Flush(waitForFlush);
}
}
flushingLogFiles = false;
}
bool
CNetwork::CreateNetworkHeap()
{
gnetDebug1("Create Network Heap");
bool success = false;
USE_MEMBUCKET(MEMBUCKET_NETWORK);
gnetAssertf(!sm_pNetworkHeap, "Network heap has already been created");
AcquireNetworkHeapMemory();
if(sm_pHeapBuffer)
{
sm_pNetworkHeap = rage_new sysMemSimpleAllocator(sm_pHeapBuffer, NETWORK_HEAP_SIZE, sysMemSimpleAllocator::HEAP_NET);
#if RAGE_TRACKING
diagTracker* t = diagTracker::GetCurrent();
if (t && sysMemVisualize::GetInstance().HasNetwork())
{
t->InitHeap("Network Heap", sm_pHeapBuffer, NETWORK_HEAP_SIZE);
}
#endif // RAGE_TRACKING
#if __ASSERT
sm_pNetworkHeap->BeginLayer();
#endif //__ASSERT
success = true;
}
return success;
}
void
CNetwork::DeleteNetworkHeap()
{
gnetDebug1("Delete Network Heap");
gnetAssertf(sm_pNetworkHeap, "Network heap doesn't exist");
gnetAssertf(!s_heapStackSize, "Network heap is still being used!");
#if __ASSERT
const int numLeaks = sm_pNetworkHeap->EndLayer("CNetwork Heap", NULL);
gnetAssertf(0 == numLeaks, "CNetwork::DeleteNetworkHeap(): Network heap leaked:%d allocations", numLeaks);
#endif //__ASSERT
RelinquishNetworkHeapMemory();
delete sm_pNetworkHeap;
sm_pNetworkHeap = 0;
}
void CNetwork::UseNetworkHeap(NetworkMemoryCategory BANK_ONLY(category))
{
gnetAssertf(sm_pNetworkHeap != 0, "Network heap doesn't exist");
sysMemStartNetwork();
#if __BANK
gnetAssertf(CNetwork::GetNetworkHeap() != NULL, "Network heap does not exist!");
NetworkMemoryCategory currentCategory = s_MemCategoryStack[s_CurrentMemCategory];
if(CNetwork::GetNetworkHeap() != NULL)
{
int currentHeapUsage = int(CNetwork::GetNetworkHeap()->GetHeapSize() - CNetwork::GetNetworkHeap()->GetMemoryAvailable());
if((category != currentCategory) && (currentCategory != NET_MEM_NUM_MANAGERS))
{
if(currentHeapUsage >= s_PreviousHeapUsage)
{
NetworkDebug::RegisterMemoryAllocated(currentCategory, (currentHeapUsage - s_PreviousHeapUsage));
}
else
{
NetworkDebug::RegisterMemoryDeallocated(currentCategory, (s_PreviousHeapUsage - currentHeapUsage));
}
}
if(gnetVerifyf(s_CurrentMemCategory < (MAX_CATEGORY_STACK_LEVELS - 1), "Memory tracking stack depth is too high!"))
{
s_CurrentMemCategory++;
s_MemCategoryStack[s_CurrentMemCategory] = category;
}
s_PreviousHeapUsage = currentHeapUsage;
}
#endif // __BANK
}
void
CNetwork::StopUsingNetworkHeap()
{
sysMemEndNetwork();
#if __BANK
if(sm_pNetworkHeap)
{
NetworkMemoryCategory currentCategory = s_MemCategoryStack[s_CurrentMemCategory];
if(currentCategory != NET_MEM_NUM_MANAGERS)
{
int currentHeapUsage = int(CNetwork::GetNetworkHeap()->GetHeapSize() - CNetwork::GetNetworkHeap()->GetMemoryAvailable());
if(currentHeapUsage >= s_PreviousHeapUsage)
{
NetworkDebug::RegisterMemoryAllocated(currentCategory, (currentHeapUsage - s_PreviousHeapUsage));
}
else
{
NetworkDebug::RegisterMemoryDeallocated(currentCategory, (s_PreviousHeapUsage - currentHeapUsage));
}
if(gnetVerifyf(s_CurrentMemCategory > 0, "Calling StopUsingNetworkHeap() too many times!"))
{
s_CurrentMemCategory--;
s_PreviousHeapUsage = currentHeapUsage;
}
}
}
#endif // __BANK
}
void CNetwork::AcquireNetworkHeapMemory()
{
if (sm_HeapBufferRefCount++ == 0)
{
gnetAssert(sm_pHeapBuffer==NULL);
#if RSG_ORBIS
sm_pHeapBuffer = sysMemFlexAllocate(NETWORK_HEAP_SIZE);
#else
gnetAssertf(sysMemAllocator::GetCurrent().GetAllocator(MEMTYPE_GAME_VIRTUAL)->GetLargestAvailableBlock() > NETWORK_HEAP_SIZE, "No block large enough to hold the network memory required");
sm_pHeapBuffer = sysMemAllocator::GetCurrent().RAGE_LOG_ALLOCATE_HEAP(NETWORK_HEAP_SIZE, 16, MEMTYPE_GAME_VIRTUAL);
#endif
}
}
void CNetwork::RelinquishNetworkHeapMemory()
{
if (--sm_HeapBufferRefCount == 0)
{
#if RSG_ORBIS
sysMemFlexFree(sm_pHeapBuffer, NETWORK_HEAP_SIZE);
#else
sysMemAllocator::GetCurrent().Free(sm_pHeapBuffer);
#endif
sm_pHeapBuffer = NULL;
}
}
sysMemAllocator*
CNetwork::GetNetworkHeap()
{
return sm_pNetworkHeap;
}
unsigned
CNetwork::GetTimeoutTime()
{
unsigned timeout = static_cast<unsigned>(DEFAULT_CONNECTION_TIMEOUT);
#if !__FINAL
unsigned t = 0;
if(PARAM_notimeouts.Get())
{
timeout = 0;
}
else if(PARAM_timeout.Get(t))
{
timeout = t * 1000;
}
#endif
return timeout;
}
#if !__FINAL
bool CNetwork::IsTimeoutModifiedByCommandLine()
{
if(PARAM_notimeouts.Get() || PARAM_timeout.Get())
return true;
return false;
}
#endif
void CNetwork::SetSyncedTimeInMilliseconds(unsigned time)
{
sm_syncedTimeInMilliseconds = time;
}
unsigned CNetwork::GetNetworkTimeLocked()
{
return sm_lockedNetworkTime;
}
unsigned CNetwork::GetNetworkTimePausable()
{
if(s_PausableNetworkTimeStopped)
{
return s_TimePausableNetworkTimeStopped;
}
else
{
return sm_lockedNetworkTime - s_PausableNetworkTimeOffset;
}
}
void CNetwork::StopNetworkTimePausable()
{
if(!s_PausableNetworkTimeStopped)
{
gnetDebug1("Stopping pausable network time at %d (offset: %d)", sm_lockedNetworkTime, s_PausableNetworkTimeOffset);
s_TimePausableNetworkTimeStopped = CNetwork::GetNetworkTimePausable();
s_PausableNetworkTimeStopped = true;
}
}
void CNetwork::RestartNetworkTimePausable()
{
if(s_PausableNetworkTimeStopped)
{
gnetDebug1("Restarting pausable network time at %d (offset: %d)", sm_lockedNetworkTime - s_PausableNetworkTimeOffset, s_PausableNetworkTimeOffset);
s_TimePausableNetworkTimeStopped = 0;
s_PausableNetworkTimeStopped = false;
}
}
#if !__NO_OUTPUT
void CNetwork::DumpNetworkTimeInfo()
{
gnetDebug1("DumpNetworkTimeInfo :: NetworkTime: %u", GetNetworkTime());
gnetDebug1("DumpNetworkTimeInfo :: NetworkTimeLocked: %u", GetNetworkTimeLocked());
gnetDebug1("DumpNetworkTimeInfo :: SyncedTimeMs: %u", sm_syncedTimeInMilliseconds);
gnetDebug1("DumpNetworkTimeInfo :: FwTimeMs: %u", fwTimer::GetTimeInMilliseconds());
gnetDebug1("DumpNetworkTimeInfo :: IsDebugPaused: %s", fwTimer::IsUserPaused() ? "True" : "False");
gnetDebug1("DumpNetworkTimeInfo :: DebugPausedLastFrame: %s", sm_WasDebugPausedLastFrame ? "True" : "False");
gnetDebug1("DumpNetworkTimeInfo :: TimeWhenPaused: %u", sm_NetworkTimeWhenDebugPaused);
sm_networkClock.Dump();
}
#endif
// Name : RenderNetworkInfo
// Purpose : called during render to display network info on the screen
void CNetwork::RenderNetworkInfo()
{
if(sm_ObjectMgr)
{
sm_ObjectMgr->DisplayObjectInfo();
}
CFakePlayerNames::RenderFakePlayerNames();
}
void CNetwork::SetGoStraightToMultiplayer(bool bGoStraightToMultiplayer)
{
if(sm_bGoStraightToMultiplayer != bGoStraightToMultiplayer)
{
gnetDebug1("SetGoStraightToMultiplayer :: %s", bGoStraightToMultiplayer ? "True" : "False");
sm_bGoStraightToMultiplayer = bGoStraightToMultiplayer;
}
}
bool CNetwork::GetGoStraightToMultiplayer()
{
return sm_bGoStraightToMultiplayer;
}
void CNetwork::SetGoStraightToMPEvent(bool bGoStraightToMpEvent)
{
if(sm_bGoStraightToMpEvent != bGoStraightToMpEvent)
{
gnetDebug1("SetGoStraightToMPEvent :: %s", bGoStraightToMpEvent ? "True" : "False");
sm_bGoStraightToMpEvent = bGoStraightToMpEvent;
}
}
bool CNetwork::GetGoStraightToMPEvent( )
{
return sm_bGoStraightToMpEvent;
}
void CNetwork::SetGoStraightToMPRandomJob(bool bGoStraightToMPRandomJob)
{
if(sm_bGoStraightToMPRandomJob != bGoStraightToMPRandomJob)
{
gnetDebug1("SetGoStraightToMPRandomJob :: %s", bGoStraightToMPRandomJob ? "True" : "False");
sm_bGoStraightToMPRandomJob = bGoStraightToMPRandomJob;
}
}
bool CNetwork::GetGoStraightToMPRandomJob( )
{
return sm_bGoStraightToMPRandomJob;
}
void CNetwork::SetShutdownSessionClearsTheGoStraightToMultiplayerFlag(bool bShutdownSessionClearsTheFlag)
{
if(sm_bShutdownSessionClearsTheGoStraightToMultiplayerFlag != bShutdownSessionClearsTheFlag)
{
gnetDebug1("SetShutdownSessionClearsTheGoStraightToMultiplayerFlag :: Shutdown - %s", bShutdownSessionClearsTheFlag ? "True" : "False");
sm_bShutdownSessionClearsTheGoStraightToMultiplayerFlag = bShutdownSessionClearsTheFlag;
}
if(sm_bGoStraightToMpEvent != bShutdownSessionClearsTheFlag)
{
gnetDebug1("SetShutdownSessionClearsTheGoStraightToMultiplayerFlag :: Event %s", bShutdownSessionClearsTheFlag ? "True" : "False");
sm_bGoStraightToMpEvent = bShutdownSessionClearsTheFlag;
}
}
// Name : ResetConnectionPolicies
// Purpose :
void CNetwork::ResetConnectionPolicies(const unsigned timeout, const unsigned keepAliveInterval)
{
gnetDebug1("ResetConnectionPolicies :: Timeout: %u, KeepAliveInterval: %u", timeout, keepAliveInterval);
netConnectionPolicies policies;
policies.SetTimeout(timeout);
policies.SetKeepAliveInterval((timeout == 0) ? 0 : ((keepAliveInterval > 0) ? keepAliveInterval : CNetwork::DEFAULT_CONNECTION_KEEP_ALIVE_INTERVAL));
//Set the default policies for all new connections on all channels.
//Policies on existing connections are not changed.
sm_CxnMgr.SetDefaultConnectionPolicies(policies);
// sets up session policies based on timeout
CNetworkSession::InitRegistrationPolicies();
//Sets the policies for all existing connections.
gnetVerify(sm_CxnMgr.SetConnectionPolicies(policies));
}
#if __BANK
// Name : InitWidgets
// Purpose : Initialises network widgets
void CNetwork::InitWidgets()
{
NetworkDebug::InitWidgets();
Tunables::GetInstance().InitWidgets();
}
void CNetwork::InitLevelWidgets()
{
}
void CNetwork::ShutdownLevelWidgets()
{
}
#endif //__BANK
//private:
bool
CNetwork::OpenNetwork()
{
if(!gnetVerify(!sm_bNetworkOpen))
{
gnetError("OpenNetwork :: Network already open!");
if(sm_bFatalErrorForDoubleOpen)
Quitf(0, "OpenNetwork :: Network already open!");
// already open
return true;
}
sm_bNetworkOpening = true;
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().PushProfiler("CNetwork::OpenNetwork", PARAM_netFunctionProfilerAsserts.Get()));
unsigned profilerTreshold=0;
PARAM_netFunctionProfilerThreshold.Get(profilerTreshold);
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().SetTimeThreshold(profilerTreshold));
Displayf("OpenNetwork(): Network Heap used %" SIZETFMT "d, available %" SIZETFMT "d, size %" SIZETFMT "d", CNetwork::sm_pNetworkHeap->GetMemoryUsed(), CNetwork::sm_pNetworkHeap->GetMemoryAvailable(), CNetwork::GetNetworkHeap()->GetHeapSize());
USE_MEMBUCKET(MEMBUCKET_NETWORK);
gnetAssert(!(sm_NetworkSession && sm_NetworkSession->IsSessionCreated()));
#if __WIN32PC || RSG_DURANGO
GRCDEVICE.SetAllowBlockUpdateThread(false);
#endif
NOTFINAL_ONLY(bool bIsFirstEntryNetworkOpen = sm_bFirstEntryNetworkOpen;)
gnetDebug1("OpenNetwork :: Is First Entry: %s", sm_bFirstEntryNetworkOpen ? "False" : "True");
// Make sure that no Binks are allocated from the Network heap
NPROFILE(g_movieMgr.DeleteAll())
network_commands::ResetTrackingVariables();
NPROFILE(CNetwork::InitManagers())
NPROFILE(CMultiplayerGamerTagHud::Init(INIT_SESSION);)
#if RSG_PC
NPROFILE(CMultiplayerChat::InitSession();)
#endif
NPROFILE(NetworkDebug::OpenNetwork())
CContextMenuHelper::InitVotes();
if(!sm_bFirstEntryNetworkOpen)
{
NPROFILE(CMiniMap::ReInit()) // restart the minimap
NPROFILE(CStatsMgr::OpenNetwork())
NPROFILE(CLiveManager::GetProfileStatsMgr().OpenNetwork())
NPROFILE(NETWORK_LEGAL_RESTRICTIONS.UpdateRestrictions())
NPROFILE(gnetVerify(CNetworkTelemetry::FlushTelemetry(true)))
NPROFILE(CNetwork::InitVoice())
CPed::SetRandomPedsDropMoney( false );
// ensure that the look at talkers flags is defaulted to TRUE
CNetObjPlayer::SetLookAtTalkers(true);
// turn off the golf course
if (CMiniMap::IsInGolfMap()) // ensure golf course is turned off before MP game is initialised
{
CMiniMap::SetCurrentGolfMap(GOLF_COURSE_OFF);
}
NPROFILE(CGarages::NetworkInit())
// reset radio history, since MP history isn't compatible with SP
if(!sm_bSkipRadioResetNextOpen)
{
NPROFILE(audRadioStation::ResetHistoryForNetworkGame())
}
sm_bSkipRadioResetNextOpen = false;
NPROFILE(audNorthAudioEngine::NotifyOpenNetwork())
SBackgroundScripts::GetInstance().SetGameMode(BGScriptInfo::FLAG_MODE_MP);
#if RSG_ORBIS
NPROFILE(rlFriendsManager::Reactivate());
#elif RSG_DURANGO
NPROFILE(rlFriendsManager::CheckListForChanges(NetworkInterface::GetLocalGamerIndex()));
#endif
}
sm_bNetworkOpen = true;
sm_bMatchStarted = false;
sm_bGameInProgress = false;
if(!sm_bFirstEntryNetworkOpen)
{
NPROFILE(fwClipSetManager::StartNetworkSession())
NPROFILE(fwAnimDirectorComponentSyncedScene::StartNetworkSession())
// flag the next call to StartMatch as an entry point
sm_bFirstEntryMatchStarted = false;
}
sm_bFirstEntryNetworkOpen = true;
if(sm_bSkipRadioResetNextClose)
{
audDisplayf("CNetwork::OpenNetwork - resetting sm_bSkipRadioResetNextClose");
sm_bSkipRadioResetNextClose = false;
}
if(!gnetVerify(netInterface::IsPlayerMgrValid()))
{
gnetError("OpenNetwork :: Player Manager Invalid!");
if(sm_bFatalErrorForInvalidPlayerManager)
Quitf(0, "OpenNetwork :: Player Manager Invalid!");
}
if(!gnetVerify(netInterface::IsPlayerMgrInitialised()))
{
gnetError("OpenNetwork :: Player Manager Not Initialized!");
if(sm_bFatalErrorForInvalidPlayerManager)
Quitf(0, "OpenNetwork :: Player Manager Not Initialized!");
}
NPROFILE(GamePresenceEvents::OpenNetworking())
NPROFILE(CGameStreamMgr::GetGameStream()->FlushQueue())
sm_bDisableCarGeneratorTime = 0;
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetMessageLog(), "OPEN_NETWORK", "\r\n");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetBandwidthManager().GetLog(), "OPEN_NETWORK", "\r\n");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetEventManager().GetLog(), "OPEN_NETWORK", "\r\n");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetObjectManager().GetLog(), "OPEN_NETWORK", "\r\n");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetPlayerMgr().GetLog(), "OPEN_NETWORK", "\r\n");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetArrayManager().GetLog(), "OPEN_NETWORK", "\r\n");
static const u32 STAT_NAT_TYPE = ATSTRINGHASH("_NatType", 0xA1C09C1A);
NPROFILE(StatsInterface::SetStatData(STAT_NAT_TYPE, netHardware::GetNatType()))
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().PopProfiler("CNetwork::OpenNetwork", !bIsFirstEntryNetworkOpen ? 5000 : 1000));
BasePlayerCardDataManager::ClearCachedStats();
sm_bNetworkOpening = false;
return (sm_bNetworkOpen);
}
void
CNetwork::EnterLobby()
{
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetPlayerMgr().GetLog(), "ENTERING_LOBBY", "\r\n");
CMiniMap::UpdateWaypoint(); // clear any SP waypoint but don't remove it completely
CMessages::ClearMessages();
CMessages::ClearAllBriefMessages();
fwTimer::SetTimeWarpAll(1.0f);
}
void
CNetwork::StartMatch()
{
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetPlayerMgr().GetLog(), "STARTING_MATCH", "\r\n");
if(!gnetVerify(sm_bNetworkOpen))
{
gnetError("StartMatch :: Network closed!");
if(sm_bFatalErrorForStartWhenClosed)
Quitf(0, "StartMatch :: Network closed!");
return;
}
if(!gnetVerify(sm_bFirstEntryNetworkOpen))
{
gnetError("StartMatch :: Network fully closed!");
if(sm_bFatalErrorForStartWhenFullyClosed)
Quitf(0, "StartMatch :: Network fully closed!");
return;
}
if(!gnetVerify(sm_bManagersAreInitialised))
{
gnetError("StartMatch :: Managers not initialised!");
if(sm_bFatalErrorForStartWhenManagersShutdown)
Quitf(0, "StartMatch :: Managers not initialised!");
return;
}
if(!gnetVerify(sm_NetworkSession && sm_NetworkSession->CanStart()))
{
gnetError("StartMatch :: Sesson not ready to start!");
}
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().PushProfiler("CNetwork::StartMatch", PARAM_netFunctionProfilerAsserts.Get()));
unsigned profilerTreshold=0;
PARAM_netFunctionProfilerThreshold.Get(profilerTreshold);
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().SetTimeThreshold(profilerTreshold));
NOTFINAL_ONLY(bool bFirstEntryMatchStarted = sm_bFirstEntryMatchStarted;)
gnetDebug1("StartMatch :: Is First Entry: %s", sm_bFirstEntryMatchStarted ? "False" : "True");
if (gnetVerify(!sm_bMatchStarted))
{
gnetAssertf(CGameWorld::GetMainPlayerInfo(), "Null player info.");
CPed* pPlayerPed = FindPlayerPed();
gnetAssertf(pPlayerPed, "Null player ped.");
//This needs to be reset in the player - If this flag is set cops don't scan for crooks and their wanted levels
if (pPlayerPed && pPlayerPed->IsLocalPlayer())
{
pPlayerPed->SetBlockingOfNonTemporaryEvents(false);
}
// Remove all population if we are joining as a client. This is a quick fix which may become more sophisticated in future (we may wish to keep our
// population if we are not near any other players, etc)
// TODO: this needs looked into. There may be more population that is allowed in a network game.
//if (!NetworkInterface::IsHost())
{
// gnetAssertf(GtaThread::CountSinglePlayerOnlyScripts() == 0, "CNetwork::StartMatch - there are still single player scripts running");
NPROFILE(CNetwork::ClearPopulation(false))
}
if(!sm_bFirstEntryMatchStarted)
{
// Make sure we clear out any stored door state (locked open or closed)
NPROFILE(CDoorSystem::Shutdown())
NPROFILE(CDoorSystem::Init())
NPROFILE(EXTRACONTENT.ExecuteScriptPatch())
NPROFILE(EXTRACONTENT.ExecuteWeaponPatchMP(true))
NPROFILE(CTexLod::FlushAllUpgrades())
// Clean out the static bounds store so that we don't have any issues where the weapon map collision
// has streamed out but the corresponding mover collision hasn't had a chance to take on the extra collision flag.
if(PARAM_mapMoverOnlyInMultiplayer.Get())
{
NPROFILE(g_StaticBoundsStore.RemoveAll())
}
}
//Register our player with the object manager
NPROFILE(NetworkInterface::RegisterObject(pPlayerPed, 0, netObject::GLOBALFLAG_PERSISTENTOWNER|CNetObjGame::GLOBALFLAG_SCRIPTOBJECT|netObject::GLOBALFLAG_CLONEALWAYS))
if (!pPlayerPed->GetNetworkObject()->CanSynchronise(true))
{
gnetWarning("StartMatch :: **WARNING**: PLAYER STILL HAS SINGLE PLAYER MODEL. This may cause player sync issues.");
}
// Reset ped groups and assign one for our player (this has to be done after the player ped is registered so we can get the physical player index)
NPROFILE(CPedGroups::Init())
CPlayerSyncTree* pPlayerSyncTree = SafeCast(CPlayerSyncTree, pPlayerPed->GetNetworkObject()->GetSyncTree());
// have to initialise the current state buffer after we have set up the players default group
NPROFILE(pPlayerSyncTree->InitialiseInitialStateBuffer(pPlayerPed->GetNetworkObject(), true))
// if the player is invisible when the network starts, force an update of the visibility state
// The player may have no sync data if he still has the single player model
if (!pPlayerPed->GetIsVisible())
{
gnetWarning("StartMatch :: Player is invisible on registration");
if (pPlayerPed->GetNetworkObject()->HasSyncData())
{
NPROFILE(pPlayerSyncTree->DirtyNode(pPlayerPed->GetNetworkObject(), *pPlayerSyncTree->GetPhysicalGameStateNode()))
}
else
{
gnetWarning("StartMatch :: **WARNING**: Could not dirty visibility node");
}
}
// register any existing population
if(!sm_bFirstEntryMatchStarted)
{
NPROFILE(CVehicleModelInfo::LoadResidentObjects())
}
NPROFILE(CVehiclePopulation::NetworkRegisterAllVehicles())
NPROFILE(CPedPopulation::NetworkRegisterAllPedsAndDummies())
NPROFILE(CObject::NetworkRegisterAllObjects())
NPROFILE(CPedPopulation::StartSpawningPeds())
NPROFILE(CVehiclePopulation::StartSpawningVehs())
static const unsigned CARGENERATOR_DISABLE_TIME = 5000;
sm_bDisableCarGeneratorTime = CARGENERATOR_DISABLE_TIME;
NPROFILE(CTaskNMBehaviour::StartNetworkGame())
NPROFILE(CPickupManager::SessionReset());
// register static arrays
if(gnetVerifyf(sm_ArrayMgr, "Array manager not initialised!"))
{
NPROFILE(sm_ArrayMgr->RegisterAllGameArrays())
}
// this needs reset when we are a cheater
if(IsCheater())
{
NPROFILE(CStatsMgr::ResetCRCStats())
}
//Remote Profile stats manager
NPROFILE(CStatsMgr::GetStatsDataMgr().StartNetworkMatch())
if(NetworkInterface::IsHost())
{
// Choose the model variation set for vehicles
int modelVariationID = fwRandom::GetRandomNumberInRange(0, NUM_NETWORK_MODEL_VARIATIONS);
gPopStreaming.SetNetworkModelVariationID(static_cast<u32>(modelVariationID));
}
NPROFILE(CTheScripts::GetScriptHandlerMgr().RegisterExistingThreads())
sm_bMatchStarted = true;
sm_bFirstEntryMatchStarted = true;
CNetGamePlayer* pMyPlayer = NetworkInterface::GetLocalPlayer();
if (pMyPlayer && pMyPlayer->IsInRoamingBubble())
{
sm_bGameInProgress = true;
}
NetworkInterface::BroadcastCachedLocalPlayerHeadBlendData();
#if __BANK
CEntity::ResetNetworkVisFlagCallStackData();
#endif
}
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().PopProfiler("CNetwork::StartMatch", bFirstEntryMatchStarted ? 5000 : 1000));
}
void
CNetwork::CloseNetwork(bool bFullClose)
{
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().PushProfiler("CNetwork::CloseNetwork", PARAM_netFunctionProfilerAsserts.Get())) ;
gnetDebug1("CloseNetwork :: Open: %s, FirstEntryOpen: %s, FullClose: %s", sm_bNetworkOpen ? "True" : "False", sm_bFirstEntryNetworkOpen ? "True" : "False", bFullClose ? "True" : "False");
if(GetObjectManager().GetForceCrashOnShutdownWhileProcessing())
{
const bool SHOULD_CRASH_DEFAULT_VAL =
#if __FINAL
false;
#else
true;
#endif
if(Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_CRASH_ON_SHUTDOWN_WHILE_PROCESSING", 0xDCFCB64C), SHOULD_CRASH_DEFAULT_VAL))
{
NETWORK_QUITF(0, "CloseNetwork is called while a sync message is being processed");
}
}
if(!gnetVerify(!sm_bNetworkOpening))
{
gnetError("CloseNetwork :: Currently opening network!");
if(sm_bFatalErrorForCloseDuringOpen)
Quitf(0, "CloseNetwork :: Currently opening network!");
// we still want to close since whatever called this will expect it
sm_bNetworkClosePending = sm_bAllowPendingCloseNetwork;
sm_bNetworkClosePendingIsFull = bFullClose;
return;
}
sm_bNetworkClosePending = false;
if(sm_bNetworkOpen || (sm_bFirstEntryNetworkOpen && bFullClose))
{
bool bWasNetworkOpen = sm_bNetworkOpen;
if(bWasNetworkOpen)
{
NPROFILE_BLOCK(WriteLogEvent);
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetMessageLog(), "CLOSE_NETWORK" "%s", bFullClose ? "Full close" : "Partial close");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetBandwidthManager().GetLog(), "CLOSE_NETWORK" "%s", bFullClose ? "Full close" : "Partial close");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetEventManager().GetLog(), "CLOSE_NETWORK" "%s", bFullClose ? "Full close" : "Partial close");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetObjectManager().GetLog(), "CLOSE_NETWORK" "%s", bFullClose ? "Full close" : "Partial close");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetPlayerMgr().GetLog(), "CLOSE_NETWORK" "%s", bFullClose ? "Full close" : "Partial close");
NetworkLogUtils::WriteLogEvent(NetworkInterface::GetArrayManager().GetLog(), "CLOSE_NETWORK" "%s", bFullClose ? "Full close" : "Partial close");
}
sm_bMatchStarted = false;
sm_bNetworkOpen = false;
sm_bGameInProgress = false;
sm_bNetworkClosing = true;
if(bFullClose)
{
// reset first entry flags
sm_bFirstEntryNetworkOpen = false;
sm_bFirstEntryMatchStarted = false;
// reset global weather flag
sm_bAppliedGlobalSettings = false;
}
NPROFILE(CMultiplayerGamerTagHud::Shutdown(SHUTDOWN_SESSION))
CContextMenuHelper::InitVotes();
#if RSG_PC
NPROFILE(CMultiplayerChat::ShutdownSession();)
#endif
if(bFullClose)
{
NPROFILE(CMiniMap::ReInit()) // restart the minimap
//Remote Profile stats manager
NPROFILE(CStatsMgr::GetStatsDataMgr().EndNetworkMatch())
BasePlayerCardDataManager::ClearCachedStats();
}
if(bWasNetworkOpen)
{
NPROFILE(CThePopMultiplierAreas::Reset())
NPROFILE(GamePresenceEvents::CloseNetworking())
NPROFILE(NetworkDebug::CloseNetwork())
NPROFILE(CNetwork::ShutdownManagers())
if(!sm_NetworkSession->IsTransitioning())
{
NPROFILE(sm_networkClock.Stop())
}
NPROFILE(CMessages::ClearMessages())
NPROFILE(CMessages::ClearAllBriefMessages())
NPROFILE(CPauseMenu::SetCurrentMissionDescription(false, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
NPROFILE(CMiniMap::SwitchOffWaypoint()) // remove any network created waypoint
NPROFILE(CScriptHud::OnNetworkClosed());
if (CMiniMap::IsInBigMap()) // turn off bigmap if it is active
{
NPROFILE(CMiniMap::SetMinimapModeState(MINIMAP_MODE_STATE_SETUP_FOR_MINIMAP))
}
}
CPed * pPlayer = FindPlayerPed();
if(bFullClose)
{
NPROFILE(CLiveManager::GetProfileStatsMgr().CloseNetwork())
NPROFILE(CStatsMgr::CloseNetwork())
NPROFILE(CNetwork::ShutdownVoice())
NPROFILE(CPed::SetRandomPedsDropMoney(true))
// Stop any pad shaking
NPROFILE(CControlMgr::StopPadsShaking())
NPROFILE(CTexLod::ShutdownSession(0))
NPROFILE(EXTRACONTENT.ExecuteWeaponPatchMP(false))
//Fix any IK issues the player is having / going to have
if (pPlayer)
{
NPROFILE(pPlayer->GetIkManager().ResetAllSolvers())
pPlayer->ClearBaseFlag(fwEntity::IS_FIXED_BY_NETWORK);
}
// need to clean up our cutscene manager prior to flushing vehicle models, as the cutscene will still be refing it.
if(CutSceneManager::GetInstancePtr())
{
NPROFILE(CutSceneManager::GetInstance()->ShutDownCutscene())
}
NPROFILE(CPickupManager::SessionReset());
NPROFILE(CTaskNMBehaviour::EndNetworkGame())
SBackgroundScripts::GetInstance().SetGameMode(BGScriptInfo::FLAG_MODE_SP);
CNetObjPlayer::ClearAllCachedPlayerHeadBlendData();
CScriptedGameEvent::ClearEventHistory();
}
else
{
CNetObjPlayer::ClearAllRemoteCachedPlayerHeadBlendData();
}
NPROFILE(CPedPopulation::StartSpawningPeds())
NPROFILE(CVehicleModelInfo::LoadResidentObjects())
NPROFILE(CVehiclePopulation::StartSpawningVehs())
NPROFILE(CVehiclePopulation::ClearInterestingVehicles())
NPROFILE(CPedGroups::RemoveAllRandomGroups())
NPROFILE(CPickupManager::RemoveAllPickups(true))
NPROFILE(COrderManager::GetInstance().ClearAllOrders())
NPROFILE(CIncidentManager::GetInstance().ClearAllIncidents())
NPROFILE(CProjectileManager::ClearAllNetSyncProjectile())
NPROFILE(CGameStreamMgr::GetGameStream()->FlushQueue())
//Reset Player Wanted Level
if (pPlayer)
{
NPROFILE(pPlayer->GetPlayerWanted()->SetWantedLevel(VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition()), WANTED_CLEAN, 0, WL_REMOVING))
}
CPlayerInfo* playerInfo = CGameWorld::GetMainPlayerInfo();
if (playerInfo)
{
//Reset Player Team
playerInfo->Team = -1;
// clear cutscene state
if (playerInfo->GetPlayerState() == CPlayerInfo::PLAYERSTATE_IN_MP_CUTSCENE)
{
playerInfo->SetPlayerState(CPlayerInfo::PLAYERSTATE_PLAYING);
}
}
SetInMPCutscene(false);
NPROFILE(audNorthAudioEngine::NotifyCloseNetwork(bFullClose))
if(bFullClose)
{
// Make sure we clear out any stored door state (locked open or closed)
NPROFILE(CDoorSystem::Shutdown())
NPROFILE(CDoorSystem::Init())
NPROFILE(fwClipSetManager::EndNetworkSession())
NPROFILE(fwAnimDirectorComponentSyncedScene::EndNetworkSession())
// force update of the synced time here to restore the single player time
UpdateSyncedTimeInMilliseconds();
// if the clock time is overridden, we also need to update the stored value for the sync time
// leave the other values unchanged
if(CNetwork::IsClockTimeOverridden())
{
int hour, min, sec, dummy = 0;
CNetwork::GetClockTimeStoredForOverride(hour, min, sec, dummy);
CNetwork::HandleClockUpdateDuringOverride(hour, min, sec, static_cast<int>(GetSyncedTimeInMilliseconds()));
}
#if !__FINAL
// ...and reset the clock so that the update timestamp is in step with the new synced time
CClock::ResetDebugModifiedInNetworkGame();
#endif
CClock::SetTime(CClock::GetHour(), CClock::GetMinute(), CClock::GetSecond());
// reset weather
NPROFILE(g_weather.NetworkShutdown())
// reset radio history, since MP history isn't compatible with SP
if(!sm_bSkipRadioResetNextClose)
{
NPROFILE(audRadioStation::ResetHistoryForSPGame())
}
// Also reset the 'skip reset on next open' flag
sm_bSkipRadioResetNextOpen = false;
// reset weapon damage modifiers|
NPROFILE(CWeaponInfoManager::ResetDamageModifiers())
}
sm_bNetworkClosing = false;
}
#if __WIN32PC|| RSG_DURANGO
GRCDEVICE.SetAllowBlockUpdateThread(true);
#endif
NOTFINAL_ONLY(netFunctionProfiler::GetProfiler().PopProfiler("CNetwork::CloseNetwork", bFullClose ? 5000 : 1000));
}
void
CNetwork::InitVoice()
{
if(!sm_NetworkVoice.IsInitialized())
{
gnetDebug1("InitVoice");
sm_NetworkVoice.InitWorkerThread();
UseNetworkHeap(NET_MEM_VOICE);
sm_NetworkVoice.Init();
StopUsingNetworkHeap();
}
}
void
CNetwork::InitManagers()
{
if (gnetVerify(!sm_bManagersAreInitialised))
{
Displayf("InitManagers(): Network Heap used %" SIZETFMT "d, available %" SIZETFMT "d, size %" SIZETFMT "d", CNetwork::GetNetworkHeap()->GetMemoryUsed(), CNetwork::GetNetworkHeap()->GetMemoryAvailable(), CNetwork::GetNetworkHeap()->GetHeapSize());
// usually 2721680 on first init, then often 2911696 on subsequent inits.
// consistently 2721680 at the end of ShutdownManagers
u32 heapUsage = (u32)(CNetwork::GetNetworkHeap()->GetHeapSize() - CNetwork::GetNetworkHeap()->GetMemoryAvailable());
gnetDebug1("CNetwork::InitManagers BEGIN --> heap usage: %u", heapUsage);
#if __ASSERT
const u32 HEAP_USAGE_THRESHOLD_ERROR = 3500000;
gnetAssertf(heapUsage <= HEAP_USAGE_THRESHOLD_ERROR, "Detecting suspicious heapUsage (%u) on InitManagers", heapUsage);
#endif //__ASSERT
netInterface::Init(sm_CxnMgr, sm_PlayerMgr, sm_networkClock, *s_MessageLog, s_QueryFunctions);
{
NPROFILE_BLOCK(CNetworkBandwidthManager::Init);
UseNetworkHeap(NET_MEM_BANDWIDTH_MANAGER);
sm_BandwidthMgr.Init(&sm_CxnMgr);
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(CNetworkPlayerMgr::Init);
netPlayerMgrBase::PhysicalOnRemoteCallback cb;
cb.Bind(&CNetworkObjectMgr::IsLocalPlayerPhysicalOnRemoteMachine);
sm_PlayerMgr.Init(&sm_CxnMgr, NETWORK_GAME_CHANNEL_ID, cb);
}
{
NPROFILE_BLOCK(CNetworkObjectMgr::Init);
UseNetworkHeap(NET_MEM_OBJECT_MANAGER);
sm_ObjectMgr = rage_new CNetworkObjectMgr(sm_BandwidthMgr);
sm_ObjectMgr->Init();
netInterface::SetObjectManager(*sm_ObjectMgr);
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(netEventMgr::Init);
UseNetworkHeap(NET_MEM_EVENT_MANAGER);
sm_EventMgr = rage_new netEventMgr(sm_BandwidthMgr);
netInterface::SetEventManager(*sm_EventMgr);
sm_EventMgr->Init();
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(CGameArrayMgr::Init);
UseNetworkHeap(NET_MEM_ARRAY_MANAGER);
sm_ArrayMgr = rage_new CGameArrayMgr(sm_BandwidthMgr);
sm_ArrayMgr->Init();
netInterface::SetArrayManager(*sm_ArrayMgr);
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(CRoamingBubbleMgr::Init);
UseNetworkHeap(NET_MEM_ROAMING_BUBBLE_MANAGER);
sm_RoamingBubbleMgr.Init();
StopUsingNetworkHeap();
}
#if __WIN32PC
NPROFILE(sm_NetworkTextChat.Init(&sm_CxnMgr))
#endif
NPROFILE(CTheScripts::GetScriptHandlerMgr().NetworkInit())
NPROFILE(CNetworkStreamingRequestManager::Init())
NPROFILE(NetworkEventTypes::RegisterNetworkEvents())
NPROFILE(UIWorldIconManager::Open())
sm_bManagersAreInitialised = true;
if (NetworkInterface::GetVoice().IsInitialized())
{
NetworkInterface::GetVoice().SetTeamOnlyChat(false);
}
NPROFILE(NetworkHasherUtil::InitHasherThread())
heapUsage = (u32)(CNetwork::GetNetworkHeap()->GetHeapSize() - CNetwork::GetNetworkHeap()->GetMemoryAvailable());
gnetDebug1("CNetwork::InitManagers END --> heap usage: %u", heapUsage);
}
}
void CNetwork::ShutdownVoice(bool bForce)
{
if(bForce || !(sm_NetworkVoiceSession.IsInVoiceSession() ||
sm_NetworkSession->IsSessionActive() ||
sm_NetworkSession->IsTransitionActive() ||
sm_NetworkParty->IsInParty()))
{
gnetDebug1("ShutdownVoice :: Force: %s", bForce ? "True" : "False");
sm_NetworkVoice.ShutdownWorkerThread();
UseNetworkHeap(NET_MEM_VOICE);
sm_NetworkVoice.Shutdown();
StopUsingNetworkHeap();
}
}
void CNetwork::ShutdownManagers()
{
if (gnetVerify(sm_bManagersAreInitialised))
{
u32 heapUsage = (u32)(CNetwork::GetNetworkHeap()->GetHeapSize() - CNetwork::GetNetworkHeap()->GetMemoryAvailable());
gnetDebug1("CNetwork::ShutdownManagers BEGIN --> heap usage: %u", heapUsage);
netInterface::SetShuttingDown();
NPROFILE(NetworkHasherUtil::ShutdownHasherThread())
NPROFILE(UIWorldIconManager::Close())
NPROFILE(CNetworkStreamingRequestManager::Shutdown())
// it would be better to pass an event to the scripts code here
NPROFILE(CTheScripts::GetScriptHandlerMgr().NetworkShutdown())
{
NPROFILE_BLOCK(CRoamingBubbleMgr::Shutdown);
UseNetworkHeap(NET_MEM_ROAMING_BUBBLE_MANAGER);
sm_RoamingBubbleMgr.Shutdown();
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(netBandwidthMgr::Shutdown);
UseNetworkHeap(NET_MEM_BANDWIDTH_MANAGER);
sm_BandwidthMgr.Shutdown();
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(CGameArrayMgr::Shutdown);
UseNetworkHeap(NET_MEM_ARRAY_MANAGER);
sm_ArrayMgr->Shutdown();
delete sm_ArrayMgr;
sm_ArrayMgr = 0;
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(netEventMgr::Shutdown);
UseNetworkHeap(NET_MEM_EVENT_MANAGER);
sm_EventMgr->Shutdown();
delete sm_EventMgr;
sm_EventMgr = 0;
StopUsingNetworkHeap();
}
{
NPROFILE_BLOCK(CNetworkObjectMgr::Shutdown);
UseNetworkHeap(NET_MEM_OBJECT_MANAGER);
sm_ObjectMgr->Shutdown();
delete sm_ObjectMgr;
sm_ObjectMgr = 0;
StopUsingNetworkHeap();
}
#if __WIN32PC
NPROFILE(sm_NetworkTextChat.Shutdown())
#endif
if (sm_NetworkSession)
{
sm_NetworkSession->GetSnSession().CancelDisplayNameTasks();
}
NPROFILE(sm_PlayerMgr.Shutdown())
heapUsage = (u32)(CNetwork::GetNetworkHeap()->GetHeapSize() - CNetwork::GetNetworkHeap()->GetMemoryAvailable());
gnetDebug1("CNetwork::ShutdownManagers END --> heap usage: %u", heapUsage);
NPROFILE(netInterface::Shutdown())
sm_bManagersAreInitialised = false;
}
}
void CNetwork::ClearPopulation(bool bIncludeMissionEntities)
{
CVehiclePopulation::ClearInterestingVehicles();
CObjectPopulation::DeleteAllTempObjects();
if (bIncludeMissionEntities)
{
// remove all script & ambient entities
CPedPopulation::RemoveAllPeds(CPedPopulation::PMR_ForceAllRandomAndMission);
CObjectPopulation::DeleteAllMissionObjects();
CVehiclePopulation::RemoveAllVehs(false);
}
else
{
// just remove all ambient entities
CPedPopulation::RemoveAllRandomPeds();
CVehiclePopulation::RemoveAllVehs(true);
}
CTrain::RemoveAllTrains();
CPickupManager::RemoveAllPickups(bIncludeMissionEntities);
COrderManager::GetInstance().ClearAllOrders();
CIncidentManager::GetInstance().ClearAllIncidents();
CProjectileManager::ClearAllNetSyncProjectile();
CPed* pPlayerPed = FindPlayerPed();
if (gnetVerify(pPlayerPed) && gnetVerify(pPlayerPed->GetPlayerWanted()))
{
pPlayerPed->GetPlayerWanted()->SetWantedLevel(VEC3V_TO_VECTOR3(pPlayerPed->GetTransform().GetPosition()), WANTED_CLEAN, 0, WL_REMOVING);
}
}
void CNetwork::OverrideClockTime(int hour, int minute, int second, bool bFromScript)
{
gClockOverride.Override(hour, minute, second, bFromScript);
}
void CNetwork::OverrideClockRate(u32 msPerGameMinute)
{
gClockOverride.OverrideRate(msPerGameMinute);
}
void CNetwork::ClearClockTimeOverride()
{
gClockOverride.StopOverriding();
}
bool CNetwork::IsClockTimeOverridden()
{
return gClockOverride.IsActive();
}
bool CNetwork::IsClockOverrideFromScript()
{
return gClockOverride.IsFromScript();
}
bool CNetwork::IsClockOverrideLocked()
{
return gClockOverride.IsOverrideLocked();
}
void CNetwork::SetClockOverrideLocked(bool bLocked)
{
gClockOverride.SetOverrideLocked(bLocked);
}
void CNetwork::HandleClockUpdateDuringOverride(int hour, int minute, int second, int lastMinAdded)
{
gClockOverride.SetStoredSessionTime(hour, minute, second, lastMinAdded);
}
void CNetwork::GetClockTimeStoredForOverride(int &hour, int &minute, int &second, int &lastMinAdded)
{
gClockOverride.GetStoredSessionTime(hour, minute, second, lastMinAdded);
}
void CNetwork::SetNetworkGameMode(u16 nGameMode)
{
if(sm_nGameMode != nGameMode)
{
// Reset current game mode state when switching game mode
SetNetworkGameModeState(GameModeState_None);
gnetDebug1("SetNetworkGameMode: %s (%u) -> %s (%u)", NetworkUtils::GetMultiplayerGameModeAsString(sm_nGameMode), sm_nGameMode, NetworkUtils::GetMultiplayerGameModeAsString(nGameMode), nGameMode);
//@@: location CNETWORK_SETNETWORKGAMEMODE
sm_nGameMode = nGameMode;
}
}
void CNetwork::SetNetworkGameModeState(int nGameModeState)
{
if(sm_nGameModeState != nGameModeState)
{
gnetDebug1("SetNetworkGameModeState: %s (%u) -> %s (%u)", NetworkUtils::GetMultiplayerGameModeStateAsString(sm_nGameModeState), sm_nGameModeState, NetworkUtils::GetMultiplayerGameModeStateAsString(nGameModeState), nGameModeState);
sm_nGameModeState = nGameModeState;
}
}
bool CNetwork::IsNetworkTutorialSessionTurn()
{
if(!gnetVerifyf(sm_IsInTutorialSession, "Calling IsNetworkTutorialSessionTurn() when not in a tutorial session!"))
{
return false;
}
if(s_TutorialSessionTurnDelay > 0)
{
return false;
}
// players within range are given turns to create vehicles, highest peer ID first.
// the game waits for a short period between changing turns, to help prevent
// cars being created on top of one another
const unsigned numTurns = s_NumPlayersInTutorialSession;
const unsigned localPlayerTurn = s_TutorialSessionNetworkTurn;
const unsigned DURATION_OF_TURN = 400;
const unsigned DURATION_BETWEEN_TURNS = 200;
unsigned currentTime = CNetwork::GetNetworkTime();
unsigned timeWithinTurn = currentTime % (DURATION_OF_TURN + DURATION_BETWEEN_TURNS);
currentTime /= (DURATION_OF_TURN + DURATION_BETWEEN_TURNS);
unsigned currentTurn = currentTime % (numTurns);
if((currentTurn == localPlayerTurn) && (timeWithinTurn <= DURATION_OF_TURN))
{
return true;
}
else
{
return false;
}
}
bool CNetwork::IsConcealedPlayerTurn()
{
if(!gnetVerifyf(sm_ThereAreConcealedPlayers, "Calling IsNetworkTutorialSessionTurn() when not in a tutorial session!"))
{
return false;
}
if(s_ConcealedPlayerTurnDelay > 0)
{
return false;
}
// players within range are given turns to create vehicles, highest peer ID first.
// the game waits for a short period between changing turns, to help prevent
// cars being created on top of one another
const unsigned numTurns = s_NumPlayersNotConcealedToMe;
const unsigned localPlayerTurn = s_ConcealedPlayerNetworkTurn;
const unsigned DURATION_OF_TURN = 400;
const unsigned DURATION_BETWEEN_TURNS = 200;
unsigned currentTime = CNetwork::GetNetworkTime();
unsigned timeWithinTurn = currentTime % (DURATION_OF_TURN + DURATION_BETWEEN_TURNS);
currentTime /= (DURATION_OF_TURN + DURATION_BETWEEN_TURNS);
unsigned currentTurn = currentTime % (numTurns);
if((currentTurn == localPlayerTurn) && (timeWithinTurn <= DURATION_OF_TURN))
{
return true;
}
else
{
return false;
}
}
void CNetwork::UpdateConcealedPlayerCachedVariables()
{
s_NumPlayersNotConcealedToMe = 0;
s_ConcealedPlayerNetworkTurn = 0;
if (NetworkInterface::AreThereAnyConcealedPlayers())
{
// if we have entered a tutorial session this frame we need to wait a while
// before allow us to think it is our turn - this gives time for remote players
// to be informed about of change in session state and avoid turn conflicts
if(!sm_ThereAreConcealedPlayers)
{
const unsigned TUTORIAL_SESSION_TURN_DELAY = 1000;
s_ConcealedPlayerTurnDelay = TUTORIAL_SESSION_TURN_DELAY;
}
else if(s_ConcealedPlayerTurnDelay > 0)
{
s_ConcealedPlayerTurnDelay -= MIN(s_ConcealedPlayerTurnDelay, fwTimer::GetSystemTimeStepInMilliseconds());
}
sm_ThereAreConcealedPlayers = true;
unsigned numRemotePhysicalPlayers = netInterface::GetNumRemotePhysicalPlayers();
const netPlayer * const *remotePhysicalPlayers = netInterface::GetRemotePhysicalPlayers();
for(unsigned index = 0; index < numRemotePhysicalPlayers; index++)
{
const CNetGamePlayer *player = SafeCast(const CNetGamePlayer, remotePhysicalPlayers[index]);
const CPed* pPlayerPed = player ? player->GetPlayerPed() : NULL;
const CNetObjPhysical* pNetObjPhysical = pPlayerPed ? static_cast<const CNetObjPhysical*>(pPlayerPed->GetNetworkObject()) : NULL;
if(pNetObjPhysical && !pNetObjPhysical->IsConcealed())
{
if (NetworkInterface::GetLocalPlayer()->GetRlGamerId() < player->GetRlGamerId())
{
s_ConcealedPlayerNetworkTurn++;
}
s_NumPlayersNotConcealedToMe++;
}
}
// include the local player
s_NumPlayersNotConcealedToMe++;
// check if it is our turn between all non concealed players
if(IsConcealedPlayerTurn())
{
s_WasConcealedPlayerNetworkTurn = true;
}
else if(s_WasConcealedPlayerNetworkTurn)
{
CTheCarGenerators::ClearScheduledQueue();
s_WasConcealedPlayerNetworkTurn = false;
}
}
else
{
sm_ThereAreConcealedPlayers = false;
s_WasConcealedPlayerNetworkTurn = false;
}
}
void CNetwork::UpdateTutorialSessionCachedVariables()
{
CNetObjPlayer *netObjPlayer = FindPlayerPed() ? static_cast<CNetObjPlayer *>(FindPlayerPed()->GetNetworkObject()) : 0;
s_NumPlayersInTutorialSession = 0;
s_TutorialSessionNetworkTurn = 0;
if(netObjPlayer)
{
netObjPlayer->UpdatePendingTutorialSession();
}
if(netObjPlayer && netObjPlayer->IsInTutorialSession())
{
// if we have entered a tutorial session this frame we need to wait a while
// before allow us to think it is our turn - this gives time for remote players
// to be informed about of change in session state and avoid turn conflicts
if(!sm_IsInTutorialSession)
{
const unsigned TUTORIAL_SESSION_TURN_DELAY = 1000;
s_TutorialSessionTurnDelay = TUTORIAL_SESSION_TURN_DELAY;
}
else if(s_TutorialSessionTurnDelay > 0)
{
s_TutorialSessionTurnDelay -= MIN(s_TutorialSessionTurnDelay, fwTimer::GetSystemTimeStepInMilliseconds());
}
sm_IsInTutorialSession = true;
unsigned numRemotePhysicalPlayers = netInterface::GetNumRemotePhysicalPlayers();
const netPlayer * const *remotePhysicalPlayers = netInterface::GetRemotePhysicalPlayers();
for(unsigned index = 0; index < numRemotePhysicalPlayers; index++)
{
const CNetGamePlayer *player = SafeCast(const CNetGamePlayer, remotePhysicalPlayers[index]);
if(!player->IsInDifferentTutorialSession())
{
if (NetworkInterface::GetLocalPlayer()->GetRlGamerId() < player->GetRlGamerId())
{
s_TutorialSessionNetworkTurn++;
}
s_NumPlayersInTutorialSession++;
}
}
// include the local player
s_NumPlayersInTutorialSession++;
// check if it is our tutorial session turn
if(IsNetworkTutorialSessionTurn())
{
s_WasTutorialSessionNetworkTurn = true;
}
else if(s_WasTutorialSessionNetworkTurn)
{
CTheCarGenerators::ClearScheduledQueue();
s_WasTutorialSessionNetworkTurn = false;
}
}
else
{
sm_IsInTutorialSession = false;
s_WasTutorialSessionNetworkTurn = false;
}
}
void CNetwork::UpdateSyncedTimeInMilliseconds()
{
// update the synced timer - available for use in single player and network games
if(NetworkInterface::IsNetworkOpen() && (NetworkInterface::IsHost() || NetworkInterface::NetworkClockHasSynced()))
{
sm_syncedTimeInMilliseconds = NetworkInterface::GetNetworkTime();
}
else
{
sm_syncedTimeInMilliseconds = fwTimer::GetTimeInMilliseconds();
}
}
void
CNetwork::OnNetEvent(netConnectionManager* /*cxnMgr*/, const netEvent* evt)
{
const unsigned eid = evt->GetId();
// log out all connection events here
netCxnEventDebug("OnNetEvent :: %s[%u] - %d::%08x - %u%s [%s]", evt->GetAutoIdNameFromId(eid),
eid,
evt->m_ChannelId,
evt->m_CxnId,
(NET_EVENT_FRAME_RECEIVED == eid) ? evt->m_FrameReceived->m_Sequence : 0,
(NET_EVENT_FRAME_RECEIVED == eid) ? (((evt->m_FrameReceived->m_Flags & NET_SEND_RELIABLE) != 0) ? "R" : "U") : "",
(NET_EVENT_FRAME_RECEIVED == eid) ? (netMessage::IsMessage(evt->m_FrameReceived->m_Payload, evt->m_FrameReceived->m_SizeofPayload) ? netMessage::GetName(evt->m_FrameReceived->m_Payload, evt->m_FrameReceived->m_SizeofPayload) : "") : "");
if(NET_EVENT_OUT_OF_MEMORY == eid)
{
const netEventOutOfMemory* pOutOfMemoryEvent = evt->m_OutOfMemory;
gnetDebug1("CxnEvent :: OutOfMemory - [0x%08x][%s], Fatal: %s", evt->m_CxnId, NetworkUtils::GetChannelAsString(evt->m_ChannelId), pOutOfMemoryEvent->m_isFatal ? "True" : "False");
// check if this is fatal
if(pOutOfMemoryEvent->m_isFatal)
{
gnetError("CxnEvent :: Fatal Out of Memory Error - Size: %u, Used: %d, Available: %d, Largest Block: %d",
static_cast<int>(s_SCxnMgrAllocator->GetHeapSize()),
static_cast<int>(s_SCxnMgrAllocator->GetMemoryUsed(-1)),
static_cast<int>(s_SCxnMgrAllocator->GetMemoryAvailable()),
static_cast<int>(s_SCxnMgrAllocator->GetLargestAvailableBlock()));
// dump all connection manager queues
sm_CxnMgr.DumpStats();
#if !__FINAL
// dump out allocator memory usage
s_SCxnMgrAllocator->PrintMemoryUsage();
#endif
// work out what to do
switch(evt->m_ChannelId)
{
case NETWORK_VOICE_CHANNEL_ID:
// don't care
break;
case NETWORK_SESSION_VOICE_CHANNEL_ID:
// just kill the voice session
if(sm_NetworkVoiceSession.IsInVoiceSession())
{
gnetWarning("CxnEvent :: Bailing from voice session. Out of Memory.");
sm_NetworkVoiceSession.Bail(true);
}
break;
case NETWORK_GAME_CHANNEL_ID:
case NETWORK_SESSION_ACTIVITY_CHANNEL_ID:
case NETWORK_SESSION_GAME_CHANNEL_ID:
{
// all multiplayer critical - abort, abort
if(NetworkInterface::IsAnySessionActive() || IsNetworkOpen())
{
gnetWarning("CxnEvent :: Bailing from multiplayer. Out of memory");
CNetwork::Bail(sBailParameters(BAIL_NETWORK_ERROR, BAIL_CTX_NETWORK_ERROR_OUT_OF_MEMORY, evt->m_ChannelId));
}
}
break;
default:
gnetAssertf(0, "CxnEvent :: Unknown channel ID (%d)", evt->m_ChannelId);
break;
}
}
}
}
#if !__NO_OUTPUT
void CNetwork::OnRequestTrackerEvent(const char* sRuleName,
const char* sRuleMatch,
const unsigned nTokenLimit,
const float nTokenRate)
{
netDebug("HttpRequestTracker: Burst detected - Rule Name: %s, Match: %s, Token Limit: %u, Token Rate: %f",
sRuleName, sRuleMatch, nTokenLimit, nTokenRate);
#if __BANK
if (!PARAM_netHttpRequestTrackerDisplayOnScreenMessage.Get())
{
return;
}
// Display a message on the screen.
char message[256];
formatf(message, "HttpRequestTracker: Burst detected - Rule Name: %s, Rule Match: %s",
sRuleName, sRuleMatch);
NetworkDebug::DisplayHttpReqTrackerMessage(message);
#endif
}
// Indicates that diagChannel has recently opened a new log file.
static bool s_LogIsYoung = false;
static sysCriticalSectionToken s_LogIsYoungToken;
// Called via a callback when a new log file is opened (not including the first one).
static void NewLogFileCallback()
{
// Can't actually do any logging in here because we're inside the logging thread.
SYS_CS_SYNC(s_LogIsYoungToken);
s_LogIsYoung = true;
}
// Write out information that it's useful to have at the top of log files. When the testers
// have been running the game for a long time it's unhelpful to have to dig backwards through
// several log files to gain some context on what might have happened.
static void WriteSummaryIfLogFileIsNew()
{
{
// Acquire lock on the NeetToWrite boolean.
SYS_CS_SYNC(s_LogIsYoungToken);
// Do nothing if the callback hasn't fired.
if (!s_LogIsYoung)
{
return;
}
s_LogIsYoung = false;
}
gnetDebug1("%s :: Log file is fresh. Writing summary...", __FUNCTION__);
CNetwork::LogSummary();
}
// Make subchannel for summary so it's easy to find.
RAGE_DEFINE_SUBCHANNEL(net, summary, DIAG_SEVERITY_DEBUG3, DIAG_SEVERITY_WARNING, DIAG_SEVERITY_ASSERT)
#define netSummaryDebug(fmt, ...) RAGE_DEBUGF1(net_summary, fmt, ##__VA_ARGS__)
// Write a quick summary of the current situation to the log.
void CNetwork::LogSummary()
{
netSummaryDebug("%s", __FUNCTION__);
if (sm_NetworkSession)
{
netSummaryDebug("Session Details: Host: %s, Established: %s",
GetNetworkSession().IsHost() ? "Local Player" : GetNetworkSession().GetHostPlayer() ? GetNetworkSession().GetHostPlayer()->GetLogName() : "Unknown",
GetNetworkSession().IsSessionEstablished() ? "True" : "False");
}
if (GetPlayerMgr().IsInitialized())
{
netSummaryDebug("Players: Physical: %u, Pending: %u, Active: %u",
GetPlayerMgr().GetNumPhysicalPlayers(),
GetPlayerMgr().GetNumPendingPlayers(),
GetPlayerMgr().GetNumActivePlayers());
{
const CNetGamePlayer* localPlayer = GetPlayerMgr().GetMyPlayer();
if (localPlayer)
{
netSummaryDebug("Local Player Name: %s", localPlayer->GetLogName());
}
else
{
netSummaryDebug("Null local player.");
}
}
}
else
{
netSummaryDebug("Player Manager not initialized.");
}
}
#endif // !__NO_OUTPUT