// moved into a helper function to ensure args is destroyed before // exit(), which may result in a memory leak. static void RunGameOrAtlas(int argc, const char* argv[]) { CmdLineArgs args(argc, argv); // We need to initialise libxml2 in the main thread before // any thread uses it. So initialise it here before we // might run Atlas. CXeromyces::Startup(); // run Atlas (if requested via args) bool ran_atlas = ATLAS_RunIfOnCmdLine(args, false); // Atlas handles the whole init/shutdown/etc sequence by itself; // when we get here, it has exited and we're done. if(ran_atlas) return; // run non-visual simulation replay if requested if (args.Has("replay")) { // TODO: Support mods Paths paths(args); g_VFS = CreateVfs(20 * MiB); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); g_VFS->Mount(L"", paths.RData()/"mods"/"public", VFS_MOUNT_MUST_EXIST); { CReplayPlayer replay; replay.Load(args.Get("replay")); replay.Replay(); } g_VFS.reset(); CXeromyces::Terminate(); return; } // run in archive-building mode if requested if (args.Has("archivebuild")) { Paths paths(args); OsPath mod(args.Get("archivebuild")); OsPath zip; if (args.Has("archivebuild-output")) zip = args.Get("archivebuild-output"); else zip = mod.Filename().ChangeExtension(L".zip"); CArchiveBuilder builder(mod, paths.Cache()); builder.Build(zip, args.Has("archivebuild-compress")); CXeromyces::Terminate(); return; } const double res = timer_Resolution(); g_frequencyFilter = CreateFrequencyFilter(res, 30.0); // run the game Init(args, 0); InitGraphics(args, 0); MainControllerInit(); while(!quit) Frame(); Shutdown(0); MainControllerShutdown(); if (restart_in_atlas) { ATLAS_RunIfOnCmdLine(args, true); return; } // Shut down libxml2 (done here to match the Startup call) CXeromyces::Terminate(); }
// moved into a helper function to ensure args is destroyed before // exit(), which may result in a memory leak. static void RunGameOrAtlas(int argc, const char* argv[]) { CmdLineArgs args(argc, argv); g_args = args; if (args.Has("version") || args.Has("-version")) { debug_printf("Pyrogenesis %s\n", engine_version); return; } const bool isVisualReplay = args.Has("replay-visual"); const bool isNonVisualReplay = args.Has("replay"); const CStr replayFile = isVisualReplay ? args.Get("replay-visual") : isNonVisualReplay ? args.Get("replay") : ""; if (isVisualReplay || isNonVisualReplay) { if (!FileExists(OsPath(replayFile))) { debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str()); return; } if (DirectoryExists(OsPath(replayFile))) { debug_printf("ERROR: The requested replay file '%s' is a directory!\n", replayFile.c_str()); return; } } // We need to initialize SpiderMonkey and libxml2 in the main thread before // any thread uses them. So initialize them here before we might run Atlas. ScriptEngine scriptEngine; CXeromyces::Startup(); if (ATLAS_RunIfOnCmdLine(args, false)) { CXeromyces::Terminate(); return; } if (isNonVisualReplay) { if (!args.Has("mod")) { LOGERROR("At least one mod should be specified! Did you mean to add the argument '-mod=public'?"); CXeromyces::Terminate(); return; } Paths paths(args); g_VFS = CreateVfs(20 * MiB); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); MountMods(paths, GetMods(args, INIT_MODS)); { CReplayPlayer replay; replay.Load(replayFile); replay.Replay( args.Has("serializationtest"), args.Has("rejointest") ? args.Get("rejointest").ToInt() : -1, args.Has("ooslog")); } g_VFS.reset(); CXeromyces::Terminate(); return; } // run in archive-building mode if requested if (args.Has("archivebuild")) { Paths paths(args); OsPath mod(args.Get("archivebuild")); OsPath zip; if (args.Has("archivebuild-output")) zip = args.Get("archivebuild-output"); else zip = mod.Filename().ChangeExtension(L".zip"); CArchiveBuilder builder(mod, paths.Cache()); // Add mods provided on the command line // NOTE: We do not handle mods in the user mod path here std::vector<CStr> mods = args.GetMultiple("mod"); for (size_t i = 0; i < mods.size(); ++i) builder.AddBaseMod(paths.RData()/"mods"/mods[i]); builder.Build(zip, args.Has("archivebuild-compress")); CXeromyces::Terminate(); return; } const double res = timer_Resolution(); g_frequencyFilter = CreateFrequencyFilter(res, 30.0); // run the game int flags = INIT_MODS; do { restart = false; quit = false; if (!Init(args, flags)) { flags &= ~INIT_MODS; Shutdown(SHUTDOWN_FROM_CONFIG); continue; } InitGraphics(args, 0); MainControllerInit(); while (!quit) Frame(); Shutdown(0); MainControllerShutdown(); flags &= ~INIT_MODS; } while (restart); if (restart_in_atlas) ATLAS_RunIfOnCmdLine(args, true); CXeromyces::Terminate(); }
void CReplayManager::RestoreSnapshot() { float watchDuration = gpGlobals->curtime - m_flReplayStartTime; if (watchDuration > m_flRunDuration) { if (m_nReplayRunIndex < m_nMaxReplayRuns - 1) { m_nReplayRunIndex += 1; m_bIsReplayStart = true; } else if (m_bIsHighlightReplay) { m_nReplayIndex = FindNextHighlightReplayIndex(m_nReplayIndex + 1, SDKGameRules()->State_Get()); if (m_nReplayIndex == -1) { StopReplay(); return; } m_nReplayRunIndex = 0; m_bIsReplayStart = true; m_bIsHighlightStart = true; } else { StopReplay(); return; } } MatchEvent *pMatchEvent = m_MatchEvents[m_nReplayIndex]; if (m_bIsReplayStart) { m_bIsReplayStart = false; m_bAtMinGoalPos = pMatchEvent->atMinGoalPos; m_bIsReplaying = true; m_bReplayIsPending = false; CalcMaxReplayRunsAndDuration(pMatchEvent, gpGlobals->curtime); watchDuration = 0; if (m_bIsHighlightReplay && m_bIsHighlightStart) { m_bIsHighlightStart = false; if (pMatchEvent->matchEventType == MATCH_EVENT_GOAL) { IGameEvent *pEvent = gameeventmanager->CreateEvent("highlight_goal"); if (pEvent) { pEvent->SetInt("second", pMatchEvent->second); pEvent->SetInt("match_period", pMatchEvent->matchPeriod); pEvent->SetInt("scoring_team", pMatchEvent->team); pEvent->SetString("scorer", pMatchEvent->pPlayer1Data ? pMatchEvent->pPlayer1Data->m_szName : ""); pEvent->SetString("first_assister", pMatchEvent->pPlayer2Data ? pMatchEvent->pPlayer2Data->m_szName : ""); pEvent->SetString("second_assister", pMatchEvent->pPlayer3Data ? pMatchEvent->pPlayer3Data->m_szName : ""); gameeventmanager->FireEvent(pEvent); } } else if (pMatchEvent->matchEventType == MATCH_EVENT_OWNGOAL) { IGameEvent *pEvent = gameeventmanager->CreateEvent("highlight_owngoal"); if (pEvent) { pEvent->SetInt("second", pMatchEvent->second); pEvent->SetInt("match_period", pMatchEvent->matchPeriod); pEvent->SetInt("scoring_team", pMatchEvent->team); pEvent->SetString("scorer", pMatchEvent->pPlayer1Data ? pMatchEvent->pPlayer1Data->m_szName : ""); gameeventmanager->FireEvent(pEvent); } } else if (pMatchEvent->matchEventType == MATCH_EVENT_MISS) { IGameEvent *pEvent = gameeventmanager->CreateEvent("highlight_miss"); if (pEvent) { pEvent->SetInt("second", pMatchEvent->second); pEvent->SetInt("match_period", pMatchEvent->matchPeriod); pEvent->SetInt("finishing_team", pMatchEvent->team); pEvent->SetString("finisher", pMatchEvent->pPlayer1Data ? pMatchEvent->pPlayer1Data->m_szName : ""); pEvent->SetString("first_assister", pMatchEvent->pPlayer2Data ? pMatchEvent->pPlayer2Data->m_szName : ""); pEvent->SetString("second_assister", pMatchEvent->pPlayer3Data ? pMatchEvent->pPlayer3Data->m_szName : ""); gameeventmanager->FireEvent(pEvent); } } else if (pMatchEvent->matchEventType == MATCH_EVENT_KEEPERSAVE) { IGameEvent *pEvent = gameeventmanager->CreateEvent("highlight_keepersave"); if (pEvent) { pEvent->SetInt("second", pMatchEvent->second); pEvent->SetInt("match_period", pMatchEvent->matchPeriod); pEvent->SetInt("keeper_team", pMatchEvent->team); pEvent->SetString("keeper", pMatchEvent->pPlayer1Data ? pMatchEvent->pPlayer1Data->m_szName : ""); pEvent->SetString("finisher", pMatchEvent->pPlayer2Data ? pMatchEvent->pPlayer2Data->m_szName : ""); gameeventmanager->FireEvent(pEvent); } } else if (pMatchEvent->matchEventType == MATCH_EVENT_REDCARD) { IGameEvent *pEvent = gameeventmanager->CreateEvent("highlight_redcard"); if (pEvent) { pEvent->SetInt("second", pMatchEvent->second); pEvent->SetInt("match_period", pMatchEvent->matchPeriod); pEvent->SetInt("fouling_team", pMatchEvent->team); pEvent->SetString("fouling_player", pMatchEvent->pPlayer1Data ? pMatchEvent->pPlayer1Data->m_szName : ""); gameeventmanager->FireEvent(pEvent); } } } } CBall *pRealBall = GetMatchBall(); if (pRealBall && !(pRealBall->GetEffects() & EF_NODRAW)) { pRealBall->AddEffects(EF_NODRAW); pRealBall->AddSolidFlags(FSOLID_NOT_SOLID); } for (int i = 1; i <= gpGlobals->maxClients; i++) { CSDKPlayer *pRealPl = ToSDKPlayer(UTIL_PlayerByIndex(i)); if (!CSDKPlayer::IsOnField(pRealPl)) continue; if (!(pRealPl->GetEffects() & EF_NODRAW)) { pRealPl->AddEffects(EF_NODRAW); pRealPl->DoServerAnimationEvent(PLAYERANIMEVENT_CANCEL); pRealPl->AddSolidFlags(FSOLID_NOT_SOLID); pRealPl->SetMoveType(MOVETYPE_NONE); } if (pRealPl->GetPlayerBall() && !(pRealPl->GetPlayerBall()->GetEffects() & EF_NODRAW)) { pRealPl->GetPlayerBall()->AddEffects(EF_NODRAW); pRealPl->GetPlayerBall()->AddSolidFlags(FSOLID_NOT_SOLID); } } float normalRunDuration = m_flRunDuration - m_flSlowMoDuration; if (watchDuration > normalRunDuration) watchDuration = normalRunDuration + m_flSlowMoCoeff * (watchDuration - normalRunDuration); // To find the correct snapshot calculate the time passed since we started watching the replay and match it to the right snapshot in our recording list Snapshot *pNextSnap = NULL; Snapshot *pSnap = NULL; float snapDuration = 0; // Traverse backwards looking for a recorded snapshot matching the time since replay start for (int i = pMatchEvent->snapshots.Count() - 1; i >= 0; i--) { // Save the snapshot of the previous iteration, so we have a snapshot to interpolate to when we found our target snapshot pNextSnap = pSnap; // Save the current snapshot pSnap = pMatchEvent->snapshots[i]; // Snapshots have absolute match times, so calculate the relative time span between the first recorded snapshot and the current snapshot snapDuration = pSnap->snaptime - pMatchEvent->snapshots[0]->snaptime - m_flReplayStartTimeOffset; // We usually only play the last x seconds of a replay instead of the whole thing, so subtract the start time offset from the time since the first snapshot. // The first snapshot which time span is equal or shorter than the duration since replay start is the one which should be shown next. if (snapDuration <= watchDuration) break; } // No snapshots in the list if (!pSnap) return; float nextSnapDuration; if (pNextSnap) nextSnapDuration = pNextSnap->snaptime - pMatchEvent->snapshots[0]->snaptime - m_flReplayStartTimeOffset; else nextSnapDuration = 0; BallSnapshot *pBallSnap = pSnap->pBallSnapshot; if (pBallSnap) { if (!m_pBall) { m_pBall = (CReplayBall *)CreateEntityByName("replayball"); m_pBall->Spawn(); } if (Q_strcmp(m_pBall->m_szSkinName, GetMatchBall()->GetSkinName())) Q_strncpy(m_pBall->m_szSkinName.GetForModify(), GetMatchBall()->GetSkinName(), MAX_KITNAME_LENGTH); m_pBall->VPhysicsGetObject()->SetPosition(pBallSnap->pos, pBallSnap->ang, false); m_pBall->VPhysicsGetObject()->SetVelocity(&pBallSnap->vel, &pBallSnap->rot); } else { UTIL_Remove(m_pBall); m_pBall = NULL; } float frac; if (pNextSnap) { // Calc fraction between both snapshots frac = clamp((watchDuration - snapDuration) / (nextSnapDuration - snapDuration), 0.0f, 1.0f); } else { // Exact snapshot time matched or no next snapshot to interpolate to frac = 0.0f; } for (int i = 0; i < 2; i++) { for (int j = 0; j < 11; j++) { PlayerSnapshot *pPlSnap = pSnap->pPlayerSnapshot[i][j]; if (!pPlSnap) { UTIL_Remove(m_pPlayers[i][j]); m_pPlayers[i][j] = NULL; continue; } if (!m_pPlayers[i][j]) { m_pPlayers[i][j] = (CReplayPlayer *)CreateEntityByName("replayplayer"); m_pPlayers[i][j]->Spawn(); m_pPlayers[i][j]->SetNumAnimOverlays(NUM_LAYERS_WANTED); } CReplayPlayer *pPl = m_pPlayers[i][j]; pPl->m_nTeamNumber = pPlSnap->teamNumber; pPl->m_nTeamPosIndex = pPlSnap->teamPosIndex; pPl->m_bIsKeeper = pPlSnap->isKeeper; pPl->m_nShirtNumber = pPlSnap->shirtNumber; if (Q_strcmp(pPl->m_szPlayerName, pPlSnap->pPlayerData->m_szName)) Q_strncpy(pPl->m_szPlayerName.GetForModify(), pPlSnap->pPlayerData->m_szName, MAX_PLAYER_NAME_LENGTH); if (Q_strcmp(pPl->m_szShirtName, pPlSnap->pPlayerData->m_szShirtName)) Q_strncpy(pPl->m_szShirtName.GetForModify(), pPlSnap->pPlayerData->m_szShirtName, MAX_PLAYER_NAME_LENGTH); pPl->m_nSkinIndex = pPlSnap->skinIndex; pPl->m_nBody = pPlSnap->body; PlayerSnapshot *pNextPlSnap = NULL; if (pNextSnap) pNextPlSnap = pNextSnap->pPlayerSnapshot[i][j]; if (frac > 0.0f && pNextPlSnap) { pPl->SetLocalOrigin(Lerp( frac, pPlSnap->pos, pNextPlSnap->pos )); //pPl->SetLocalVelocity(Lerp( frac, pPlSnap->vel, pNextPlSnap->vel )); pPl->SetLocalAngles(Lerp( frac, pPlSnap->ang, pNextPlSnap->ang )); } else { pPl->SetLocalOrigin(pPlSnap->pos); //pPl->SetLocalVelocity(pPlSnap->vel); pPl->SetLocalAngles(pPlSnap->ang); } bool interpolationAllowed; if (frac > 0.0f && pNextPlSnap && pPlSnap->masterSequence == pNextPlSnap->masterSequence) { // If the master state changes, all layers will be invalid too, so don't interp (ya know, interp barely ever happens anyway) interpolationAllowed = true; } else interpolationAllowed = false; // First do the master settings if (interpolationAllowed) { pPl->SetSequence( Lerp( frac, pPlSnap->masterSequence, pNextPlSnap->masterSequence ) ); pPl->SetCycle( Lerp( frac, pPlSnap->masterCycle, pNextPlSnap->masterCycle ) ); if( pPlSnap->masterCycle > pNextPlSnap->masterCycle ) { // the older record is higher in frame than the newer, it must have wrapped around from 1 back to 0 // add one to the newer so it is lerping from .9 to 1.1 instead of .9 to .1, for example. float newCycle = Lerp( frac, pPlSnap->masterCycle, pNextPlSnap->masterCycle + 1 ); pPl->SetCycle(newCycle < 1 ? newCycle : newCycle - 1 );// and make sure .9 to 1.2 does not end up 1.05 } else { pPl->SetCycle( Lerp( frac, pPlSnap->masterCycle, pNextPlSnap->masterCycle ) ); } pPl->SetPoseParameter(pPl->GetModelPtr(), 4, Lerp(frac, pPlSnap->moveX, pNextPlSnap->moveX)); pPl->SetPoseParameter(pPl->GetModelPtr(), 3, Lerp(frac, pPlSnap->moveY, pNextPlSnap->moveY)); } else { pPl->SetSequence(pPlSnap->masterSequence); pPl->SetCycle(pPlSnap->masterCycle); pPl->SetPoseParameter(pPl->GetModelPtr(), 4, pPlSnap->moveX); pPl->SetPoseParameter(pPl->GetModelPtr(), 3, pPlSnap->moveY); } // Now do all the layers for (int layerIndex = 0; layerIndex < NUM_LAYERS_WANTED; layerIndex++) { CAnimationLayer *pTargetLayer = pPl->GetAnimOverlay(layerIndex); if(!pTargetLayer) continue; LayerRecord *pSourceLayer = &pPlSnap->layerRecords[layerIndex]; pTargetLayer->m_nOrder = pSourceLayer->order; pTargetLayer->m_nSequence = pSourceLayer->sequence; pTargetLayer->m_flPlaybackRate = pSourceLayer->playbackRate; pTargetLayer->m_fFlags = pSourceLayer->flags; pTargetLayer->m_bLooping = pSourceLayer->looping; pTargetLayer->m_bSequenceFinished = pSourceLayer->sequenceFinished; pTargetLayer->m_nPriority = pSourceLayer->priority; pTargetLayer->m_nActivity = pSourceLayer->activity; pTargetLayer->m_flLastAccess = pSourceLayer->lastAccess; pTargetLayer->m_flLastEventCheck = pSourceLayer->lastEventCheck; if (interpolationAllowed) { LayerRecord *pNextSourceLayer = &pNextPlSnap->layerRecords[layerIndex]; if(pSourceLayer->order == pNextSourceLayer->order && pSourceLayer->sequence == pNextSourceLayer->sequence) { // We can't interpolate across a sequence or order change if( pSourceLayer->cycle > pNextSourceLayer->cycle ) { // the older record is higher in frame than the newer, it must have wrapped around from 1 back to 0 // add one to the newer so it is lerping from .9 to 1.1 instead of .9 to .1, for example. float newCycle = Lerp( frac, pSourceLayer->cycle, pNextSourceLayer->cycle + 1 ); pTargetLayer->m_flCycle = newCycle < 1 ? newCycle : newCycle - 1;// and make sure .9 to 1.2 does not end up 1.05 } else { pTargetLayer->m_flCycle = Lerp(frac, pSourceLayer->cycle, pNextSourceLayer->cycle); } pTargetLayer->m_flWeight = Lerp(frac, pSourceLayer->weight, pNextSourceLayer->weight); pTargetLayer->m_flLayerAnimtime = Lerp(frac, pSourceLayer->layerAnimtime, pNextSourceLayer->layerAnimtime); pTargetLayer->m_flBlendIn = Lerp(frac, pSourceLayer->blendIn, pNextSourceLayer->blendIn); pTargetLayer->m_flBlendOut = Lerp(frac, pSourceLayer->blendOut, pNextSourceLayer->blendOut); pTargetLayer->m_flPrevCycle = Lerp(frac, pSourceLayer->prevCycle, pNextSourceLayer->prevCycle); pTargetLayer->m_flKillDelay = Lerp(frac, pSourceLayer->killDelay, pNextSourceLayer->killDelay); pTargetLayer->m_flKillRate = Lerp(frac, pSourceLayer->killRate, pNextSourceLayer->killRate); pTargetLayer->m_flLayerFadeOuttime = Lerp(frac, pSourceLayer->layerFadeOuttime, pNextSourceLayer->layerFadeOuttime); } } else { //Either no interp, or interp failed. Just use record. pTargetLayer->m_flCycle = pSourceLayer->cycle; pTargetLayer->m_flWeight = pSourceLayer->weight; pTargetLayer->m_flLayerAnimtime = pSourceLayer->layerAnimtime; pTargetLayer->m_flBlendIn = pSourceLayer->blendIn; pTargetLayer->m_flBlendOut = pSourceLayer->blendOut; pTargetLayer->m_flPrevCycle = pSourceLayer->prevCycle; pTargetLayer->m_flKillDelay = pSourceLayer->killDelay; pTargetLayer->m_flKillRate = pSourceLayer->killRate; pTargetLayer->m_flLayerFadeOuttime = pSourceLayer->layerFadeOuttime; } } } } }
// moved into a helper function to ensure args is destroyed before // exit(), which may result in a memory leak. static void RunGameOrAtlas(int argc, const char* argv[]) { CmdLineArgs args(argc, argv); g_args = args; // We need to initialise libxml2 in the main thread before // any thread uses it. So initialise it here before we // might run Atlas. CXeromyces::Startup(); // run Atlas (if requested via args) bool ran_atlas = ATLAS_RunIfOnCmdLine(args, false); // Atlas handles the whole init/shutdown/etc sequence by itself; // when we get here, it has exited and we're done. if(ran_atlas) return; // run non-visual simulation replay if requested if (args.Has("replay")) { std::string replayFile = args.Get("replay"); if (!FileExists(OsPath(replayFile))) { debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str()); return; } Paths paths(args); g_VFS = CreateVfs(20 * MiB); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); MountMods(paths, GetMods(args, INIT_MODS)); { CReplayPlayer replay; replay.Load(replayFile); replay.Replay(args.Has("serializationtest"), args.Has("ooslog")); } g_VFS.reset(); CXeromyces::Terminate(); return; } // If visual replay file does not exist, quit before starting the renderer if (args.Has("replay-visual")) { std::string replayFile = args.Get("replay-visual"); if (!FileExists(OsPath(replayFile))) { debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str()); return; } } // run in archive-building mode if requested if (args.Has("archivebuild")) { Paths paths(args); OsPath mod(args.Get("archivebuild")); OsPath zip; if (args.Has("archivebuild-output")) zip = args.Get("archivebuild-output"); else zip = mod.Filename().ChangeExtension(L".zip"); CArchiveBuilder builder(mod, paths.Cache()); // Add mods provided on the command line // NOTE: We do not handle mods in the user mod path here std::vector<CStr> mods = args.GetMultiple("mod"); for (size_t i = 0; i < mods.size(); ++i) builder.AddBaseMod(paths.RData()/"mods"/mods[i]); builder.Build(zip, args.Has("archivebuild-compress")); CXeromyces::Terminate(); return; } const double res = timer_Resolution(); g_frequencyFilter = CreateFrequencyFilter(res, 30.0); // run the game int flags = INIT_MODS; do { restart = false; quit = false; if (!Init(args, flags)) { flags &= ~INIT_MODS; Shutdown(SHUTDOWN_FROM_CONFIG); continue; } InitGraphics(args, 0); MainControllerInit(); while (!quit) Frame(); Shutdown(0); MainControllerShutdown(); flags &= ~INIT_MODS; } while (restart); if (restart_in_atlas) { ATLAS_RunIfOnCmdLine(args, true); return; } // Shut down libxml2 (done here to match the Startup call) CXeromyces::Terminate(); }