Skip to content

Commit 9b5f16f

Browse files
committed
Add custom save/restore implementation for use in multiplayer mods
1 parent 888a27c commit 9b5f16f

File tree

14 files changed

+2095
-0
lines changed

14 files changed

+2095
-0
lines changed

src/game/client/client_mapbase.vpc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ $Project
4646
$File "$SRCDIR\game\shared\mapbase\mapbase_usermessages.cpp"
4747
$File "$SRCDIR\game\shared\mapbase\mapbase_rpc.cpp"
4848
$File "$SRCDIR\game\shared\mapbase\mapbase_game_log.cpp"
49+
$File "$SRCDIR\game\shared\mapbase\mapbase_mp_saverestore.cpp" [$HL2MP||$TF]
50+
$File "$SRCDIR\game\shared\mapbase\mapbase_mp_saverestore.h" [$HL2MP||$TF]
4951
$File "$SRCDIR\game\shared\mapbase\MapEdit.cpp"
5052
$File "$SRCDIR\game\shared\mapbase\MapEdit.h"
5153
$File "$SRCDIR\game\shared\mapbase\matchers.cpp"

src/game/server/NextBot/Player/NextBotPlayer.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class NextBotPlayer : public PlayerType, public INextBot, public INextBotPlayerI
145145
virtual ~NextBotPlayer();
146146

147147
virtual void Spawn( void );
148+
virtual void OnRestore( void );
148149

149150
virtual void SetSpawnPoint( CBaseEntity *spawnPoint ); // define place in environment where bot will (re)spawn
150151
virtual CBaseEntity *EntSelectSpawnPoint( void );
@@ -553,6 +554,36 @@ inline void NextBotPlayer< PlayerType >::Spawn( void )
553554
}
554555

555556

557+
//-----------------------------------------------------------------------------------------------------
558+
template < typename PlayerType >
559+
inline void NextBotPlayer< PlayerType >::OnRestore( void )
560+
{
561+
engine->SetFakeClientConVarValue( this->edict(), "cl_autohelp", "0" );
562+
563+
m_prevInputButtons = m_inputButtons = 0;
564+
m_fireButtonTimer.Invalidate();
565+
m_meleeButtonTimer.Invalidate();
566+
m_specialFireButtonTimer.Invalidate();
567+
m_useButtonTimer.Invalidate();
568+
m_reloadButtonTimer.Invalidate();
569+
m_forwardButtonTimer.Invalidate();
570+
m_backwardButtonTimer.Invalidate();
571+
m_leftButtonTimer.Invalidate();
572+
m_rightButtonTimer.Invalidate();
573+
m_jumpButtonTimer.Invalidate();
574+
m_crouchButtonTimer.Invalidate();
575+
m_walkButtonTimer.Invalidate();
576+
m_buttonScaleTimer.Invalidate();
577+
m_forwardScale = m_rightScale = 0.04;
578+
m_burningTimer.Invalidate();
579+
580+
// reset first, because Spawn() may access various interfaces
581+
INextBot::Reset();
582+
583+
BaseClass::OnRestore();
584+
}
585+
586+
556587

557588
//-----------------------------------------------------------------------------------------------------
558589
inline void _NextBot_BuildUserCommand( CUserCmd *cmd, const QAngle &viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse )

src/game/server/baseentity.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
#include "mapbase/matchers.h"
6868
#include "mapbase/datadesc_mod.h"
6969
#endif
70+
#ifdef MAPBASE_MP
71+
#include "mapbase/mapbase_mp_saverestore.h"
72+
#endif
7073
#ifdef NEW_RESPONSE_SYSTEM
7174
#include "ai_speech.h"
7275
#endif
@@ -4263,6 +4266,22 @@ int CBaseEntity::Restore( IRestore &restore )
42634266
Vector parentSpaceOffset = pGameInfo->modelSpaceOffset;
42644267
if ( !GetParent() )
42654268
{
4269+
#ifdef MAPBASE_MP
4270+
if (g_MPSaveRestore.IsTransitioning() || g_MPSaveRestore.IsRestoringPlayer())
4271+
{
4272+
// HACKHACK: m_vecOrigin isn't saved as a FIELD_POSITION_VECTOR because it's ambiguous whether it's local to the world
4273+
// or local to a parent. The existing code here is meant to fix that up, but our implementation of save/restore is having
4274+
// m_vecOrigin transition with its old map's direct origin when this code expects a relative origin.
4275+
// I have not found any code which converts m_vecOrigin to be relative to the landmark, so I'm not sure why this isn't
4276+
// already a problem in stock save/restore, although it could be related to running the game in multiplayer, or the MP
4277+
// branch in general.
4278+
// Regardless, since the local and absolute origins are meant to be the same when there's no parent anyway, we just assign
4279+
// the absolute origin to the local origin and ignore the landmark (since it was already calculated) to get around this issue.
4280+
m_vecOrigin = m_vecAbsOrigin;
4281+
}
4282+
else
4283+
#endif
4284+
42664285
// parent is the world, so parent space is worldspace
42674286
// so update with the worldspace leveltransition transform
42684287
parentSpaceOffset += pGameInfo->GetLandmark();

src/game/server/gameinterface.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@
9494
#include "world.h"
9595
#endif
9696

97+
#ifdef MAPBASE_MP
98+
#include "mapbase/mapbase_mp_saverestore.h"
99+
#endif
100+
97101
#include "vscript/ivscript.h"
98102
#include "vscript_server.h"
99103

@@ -705,6 +709,9 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory,
705709
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
706710
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAchievementSaveRestoreBlockHandler() );
707711
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() );
712+
#ifdef MAPBASE_MP
713+
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetMPPlayerSaveRestoreBlockHandler() );
714+
#endif
708715

709716
// The string system must init first + shutdown last
710717
IGameSystem::Add( GameStringSystem() );
@@ -768,6 +775,15 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory,
768775
gamestatsuploader->InitConnection();
769776
#endif
770777

778+
#ifdef MAPBASE_MP
779+
// If the last param contains .mpsav, then load it
780+
const char *pszSave = CommandLine()->GetParm( CommandLine()->ParmCount() - 1 );
781+
if ( pszSave && V_strstr( pszSave, ".mpsav" ) )
782+
{
783+
g_MPSaveRestore.StartLoadingSave( pszSave );
784+
}
785+
#endif
786+
771787
return true;
772788
}
773789

@@ -1007,6 +1023,21 @@ bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities,
10071023
//Tony; parse custom manifest if exists!
10081024
ParseParticleEffectsMap( pMapName, false );
10091025

1026+
#ifdef MAPBASE_MP
1027+
if ( g_MPSaveRestore.IsTransitioning() ) // EnabledTransitions
1028+
{
1029+
if ( g_MPSaveRestore.FindTransitionFile( pMapName, &pOldLevel, &pLandmarkName ) )
1030+
{
1031+
loadGame = true;
1032+
}
1033+
}
1034+
1035+
if ( !loadGame && g_MPSaveRestore.IsLoadingSave() )
1036+
{
1037+
loadGame = true;
1038+
}
1039+
#endif
1040+
10101041
// IGameSystem::LevelInitPreEntityAllSystems() is called when the world is precached
10111042
// That happens either in LoadGameState() or in MapEntity_ParseAllEntities()
10121043
if ( loadGame )
@@ -1023,6 +1054,23 @@ bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities,
10231054
BeginRestoreEntities();
10241055
if ( !engine->LoadGameState( pMapName, 1 ) )
10251056
{
1057+
#ifdef MAPBASE_MP
1058+
if ( g_MPSaveRestore.IsTransitioning() )
1059+
{
1060+
// If we've been to this level before, transition to it
1061+
CSaveRestoreData *pSaveData = SaveInit( 0 );
1062+
if ( pSaveData && !g_MPSaveRestore.RestoreNextLevelFile( pSaveData, pMapName, pOldLevel, pLandmarkName ) && pOldLevel )
1063+
{
1064+
// No existing level data
1065+
MapEntity_ParseAllEntities( pMapEntities );
1066+
}
1067+
}
1068+
else if ( g_MPSaveRestore.IsLoadingSave() && !pOldLevel )
1069+
{
1070+
// Do nothing and let the system handle it (don't return false)
1071+
}
1072+
else
1073+
#endif
10261074
if ( pOldLevel )
10271075
{
10281076
MapEntity_ParseAllEntities( pMapEntities );
@@ -1034,6 +1082,28 @@ bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities,
10341082
}
10351083
}
10361084

1085+
#ifdef MAPBASE_MP
1086+
if ( g_MPSaveRestore.IsTransitioning() )
1087+
{
1088+
CSaveRestoreData *pSaveData = SaveInit( 0 );
1089+
if (pSaveData)
1090+
{
1091+
g_MPSaveRestore.RestoreTransitionFile( pSaveData, pMapName, pOldLevel, pLandmarkName );
1092+
}
1093+
g_MPSaveRestore.EndTransition();
1094+
}
1095+
else if ( g_MPSaveRestore.IsLoadingSave() )
1096+
{
1097+
CSaveRestoreData *pSaveData = SaveInit( 0 );
1098+
if (pSaveData)
1099+
{
1100+
g_MPSaveRestore.LoadFile( pSaveData );
1101+
}
1102+
g_MPSaveRestore.StopLoadingSave();
1103+
}
1104+
else
1105+
#endif
1106+
10371107
if ( pOldLevel )
10381108
{
10391109
engine->LoadAdjacentEnts( pOldLevel, pLandmarkName );
@@ -1042,6 +1112,9 @@ bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities,
10421112
if ( g_OneWayTransition )
10431113
{
10441114
engine->ClearSaveDirAfterClientLoad();
1115+
#ifdef MAPBASE_MP
1116+
g_MPSaveRestore.ClearTransitionFiles();
1117+
#endif
10451118
}
10461119

10471120
if ( pOldLevel && sv_autosave.GetBool() == true )

src/game/server/hl2mp/hl2mp_client.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#include "engine/IEngineSound.h"
2626
#include "team.h"
2727
#include "viewport_panel_names.h"
28+
#ifdef MAPBASE_MP
29+
#include "mapbase/mapbase_mp_saverestore.h"
30+
#endif
2831

2932
#include "tier0/vprof.h"
3033

@@ -41,6 +44,10 @@ extern bool g_fGameOver;
4144
void FinishClientPutInServer( CHL2MP_Player *pPlayer )
4245
{
4346
pPlayer->InitialSpawn();
47+
48+
#ifdef MAPBASE_MP
49+
if ( !g_MPSaveRestore.RestorePlayer( pPlayer ) )
50+
#endif
4451
pPlayer->Spawn();
4552

4653

src/game/server/player.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
#include "mapbase/vscript_funcs_shared.h"
9898
#endif
9999

100+
#ifdef MAPBASE_MP
101+
#include "mapbase/mapbase_mp_saverestore.h"
102+
#endif
103+
100104
ConVar autoaim_max_dist( "autoaim_max_dist", "2160" ); // 2160 = 180 feet
101105
ConVar autoaim_max_deflect( "autoaim_max_deflect", "0.99" );
102106

@@ -5025,6 +5029,19 @@ void CBasePlayer::PostThink()
50255029
PostThinkVPhysics();
50265030
VPROF_SCOPE_END();
50275031
}
5032+
#ifdef MAPBASE_MP
5033+
else if (g_MPSaveRestore.IsPlayerWaitingToTransition( this ))
5034+
{
5035+
// Allow player to cancel transition
5036+
if (m_afButtonPressed & IN_JUMP)
5037+
{
5038+
if (!g_MPSaveRestore.PlayerCanCancelTransition( this ) || !g_MPSaveRestore.RemovePlayerFromTransition( this, true ))
5039+
UTIL_HudHintText( this, "#Valve_Hint_ExitTransition_Failure" );
5040+
else
5041+
UTIL_HudHintText( this, NULL ); // Clear HUD hint
5042+
}
5043+
}
5044+
#endif
50285045

50295046
#if !defined( NO_ENTITY_PREDICTION )
50305047
// Even if dead simulate entities
@@ -5668,7 +5685,16 @@ int CBasePlayer::Restore( IRestore &restore )
56685685

56695686
CSaveRestoreData *pSaveData = gpGlobals->pSaveData;
56705687
// landmark isn't present.
5688+
#ifdef MAPBASE_MP
5689+
// This interferes with bots in MP save/restore and appears to serve no purpose.
5690+
// When players are created, their physics shadow (which uses abs origin) instantly overwrites this value.
5691+
// However, bots issue commands the instant they're created, so the abs origin is overwritten before that can happen.
5692+
// Since this code seemingly assumes players always spawn at the start when there's no landmark, I'm assuming it reflects
5693+
// an older version of the saving system and isn't used.
5694+
if ( !pSaveData->levelInfo.fUseLandmark && !g_MPSaveRestore.IsRestoringPlayer( this ) )
5695+
#else
56715696
if ( !pSaveData->levelInfo.fUseLandmark )
5697+
#endif
56725698
{
56735699
Msg( "No Landmark:%s\n", pSaveData->levelInfo.szLandmarkName );
56745700

@@ -5708,6 +5734,14 @@ int CBasePlayer::Restore( IRestore &restore )
57085734
CPlayerRestoreHelper helper;
57095735
InitVCollision( helper.GetAbsOrigin( this ), helper.GetAbsVelocity( this ) );
57105736

5737+
#ifdef MAPBASE_MP
5738+
// We can't save the viewmodels in MP at the moment
5739+
CreateViewModel();
5740+
#ifdef HL2_DLL
5741+
CreateHandModel();
5742+
#endif
5743+
#endif
5744+
57115745
// success
57125746
return 1;
57135747
}
@@ -5737,6 +5771,17 @@ void CBasePlayer::OnRestore( void )
57375771
}
57385772
}
57395773

5774+
#ifdef MAPBASE_MP
5775+
//-----------------------------------------------------------------------------
5776+
// Purpose: Restores a weapon created by MP save/restore
5777+
//-----------------------------------------------------------------------------
5778+
void CBasePlayer::RestoreWeapon( CBaseCombatWeapon *pWeapon, int i )
5779+
{
5780+
pWeapon->Equip( this );
5781+
m_hMyWeapons.Set( i, pWeapon );
5782+
}
5783+
#endif
5784+
57405785
/* void CBasePlayer::SetTeamName( const char *pTeamName )
57415786
{
57425787
Q_strncpy( m_szTeamName, pTeamName, TEAM_NAME_LENGTH );

src/game/server/player.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,11 @@ class CBasePlayer : public CBaseCombatCharacter
444444
virtual bool ShouldSavePhysics();
445445
virtual void OnRestore( void );
446446

447+
#ifdef MAPBASE_MP
448+
// Used by MP save/restore only
449+
virtual void RestoreWeapon( CBaseCombatWeapon *pWeapon, int i );
450+
#endif
451+
447452
virtual void PackDeadPlayerItems( void );
448453
virtual void RemoveAllItems( bool removeSuit );
449454
bool IsDead() const;

src/game/server/server_mapbase.vpc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ $Project
4545
$File "$SRCDIR\game\shared\mapbase\mapbase_usermessages.cpp"
4646
$File "$SRCDIR\game\shared\mapbase\mapbase_rpc.cpp"
4747
$File "$SRCDIR\game\shared\mapbase\mapbase_game_log.cpp"
48+
$File "$SRCDIR\game\shared\mapbase\mapbase_mp_saverestore.cpp" [$HL2MP||$TF]
49+
$File "$SRCDIR\game\shared\mapbase\mapbase_mp_saverestore.h" [$HL2MP||$TF]
4850
$File "$SRCDIR\game\shared\mapbase\MapEdit.cpp"
4951
$File "$SRCDIR\game\shared\mapbase\MapEdit.h"
5052
$File "$SRCDIR\game\shared\mapbase\matchers.cpp"

0 commit comments

Comments
 (0)