From fa45fffa3988f4c556718debf5039674b2a203a2 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Wed, 27 Jan 2021 11:01:38 -0600 Subject: [PATCH] Added save/restore to client-side VScript --- sp/src/game/client/c_baseentity.cpp | 4 + sp/src/game/client/c_world.cpp | 82 +++++++- sp/src/game/client/c_world.h | 11 +- sp/src/game/client/cdll_client_int.cpp | 14 ++ sp/src/game/client/vscript_client.cpp | 30 ++- sp/src/game/server/vscript_server.cpp | 176 +---------------- sp/src/game/server/vscript_server.h | 12 -- sp/src/game/server/world.cpp | 15 +- sp/src/game/server/world.h | 2 +- .../shared/mapbase/logic_script_client.cpp | 26 +++ .../shared/mapbase/vscript_singletons.cpp | 19 ++ .../game/shared/mapbase/vscript_singletons.h | 4 + sp/src/game/shared/vscript_shared.cpp | 187 ++++++++++++++++++ sp/src/game/shared/vscript_shared.h | 11 ++ 14 files changed, 386 insertions(+), 207 deletions(-) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index a4a29ea1..b6253468 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -429,6 +429,7 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst ) END_RECV_TABLE() BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) #ifdef MAPBASE_VSCRIPT @@ -6130,6 +6131,9 @@ BEGIN_DATADESC_NO_BASE( C_BaseEntity ) DEFINE_FIELD( m_angAbsRotation, FIELD_VECTOR ), DEFINE_ARRAY( m_rgflCoordinateFrame, FIELD_FLOAT, 12 ), // NOTE: MUST BE IN LOCAL SPACE, NOT POSITION_VECTOR!!! (see CBaseEntity::Restore) DEFINE_FIELD( m_fFlags, FIELD_INTEGER ), +#ifdef MAPBASE_VSCRIPT + DEFINE_FIELD( m_iszScriptId, FIELD_STRING ), +#endif END_DATADESC() //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/c_world.cpp b/sp/src/game/client/c_world.cpp index cf48d47c..938db241 100644 --- a/sp/src/game/client/c_world.cpp +++ b/sp/src/game/client/c_world.cpp @@ -13,6 +13,9 @@ #include "ivieweffects.h" #include "shake.h" #include "eventlist.h" +#ifdef MAPBASE +#include "mapentities_shared.h" +#endif // NVNT haptic include for notification of world precache #include "haptics/haptic_utils.h" // memdbgon must be the last include file in a .cpp file!!! @@ -62,9 +65,6 @@ BEGIN_RECV_TABLE( C_World, DT_World ) #ifdef MAPBASE RecvPropString(RECVINFO(m_iszChapterTitle)), #endif -#ifdef MAPBASE_VSCRIPT - RecvPropInt(RECVINFO(m_iScriptLanguageClient)), -#endif END_RECV_TABLE() #ifdef MAPBASE_VSCRIPT @@ -86,6 +86,11 @@ bool C_World::Init( int entnum, int iSerialNum ) ActivityList_Init(); EventList_Init(); +#ifdef MAPBASE_VSCRIPT + m_iScriptLanguageServer = SL_NONE; + m_iScriptLanguageClient = SL_NONE; +#endif + return BaseClass::Init( entnum, iSerialNum ); } @@ -129,11 +134,6 @@ void C_World::OnDataChanged( DataUpdateType_t updateType ) engine->SetOcclusionParameters( params ); modelinfo->SetLevelScreenFadeRange( m_flMinPropScreenSpaceWidth, m_flMaxPropScreenSpaceWidth ); - -#ifdef MAPBASE_VSCRIPT - // This is now here so that C_World has time to receive the selected script language - VScriptClientInit(); -#endif } } @@ -199,6 +199,72 @@ void C_World::Spawn( void ) Precache(); } +//----------------------------------------------------------------------------- +// Parse data from a map file +//----------------------------------------------------------------------------- +bool C_World::KeyValue( const char *szKeyName, const char *szValue ) +{ +#ifdef MAPBASE_VSCRIPT + if ( FStrEq( szKeyName, "vscriptlanguage" ) ) + { + m_iScriptLanguageServer = atoi( szValue ); + } + else if ( FStrEq( szKeyName, "vscriptlanguage_client" ) ) + { + m_iScriptLanguageClient = atoi( szValue ); + } + else +#endif + return BaseClass::KeyValue( szKeyName, szValue ); + + return true; +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Parses worldspawn data from BSP on the client +//----------------------------------------------------------------------------- +void C_World::ParseWorldMapData( const char *pMapData ) +{ + char szTokenBuffer[MAPKEY_MAXLENGTH]; + for ( ; true; pMapData = MapEntity_SkipToNextEntity(pMapData, szTokenBuffer) ) + { + // + // Parse the opening brace. + // + char token[MAPKEY_MAXLENGTH]; + pMapData = MapEntity_ParseToken( pMapData, token ); + + // + // Check to see if we've finished or not. + // + if (!pMapData) + break; + + if (token[0] != '{') + { + Error( "MapEntity_ParseAllEntities: found %s when expecting {", token); + continue; + } + + CEntityMapData entData( (char*)pMapData ); + char className[MAPKEY_MAXLENGTH]; + + if (!entData.ExtractValue( "classname", className )) + { + Error( "classname missing from entity!\n" ); + } + + if ( !Q_strcmp( className, "worldspawn" ) ) + { + // Set up keyvalues. + ParseMapData( &entData ); + return; + } + } +} +#endif + C_World *GetClientWorldEntity() diff --git a/sp/src/game/client/c_world.h b/sp/src/game/client/c_world.h index a0cc315c..2174f32b 100644 --- a/sp/src/game/client/c_world.h +++ b/sp/src/game/client/c_world.h @@ -31,6 +31,7 @@ public: virtual void Precache(); virtual void Spawn(); + virtual bool KeyValue( const char *szKeyName, const char *szValue ); // Don't worry about adding the world to the collision list; it's already there virtual CollideType_t GetCollideType( void ) { return ENTITY_SHOULD_NOT_COLLIDE; } @@ -41,8 +42,15 @@ public: float GetWaveHeight() const; const char *GetDetailSpriteMaterial() const; +#ifdef MAPBASE + // A special function which parses map data for the client world entity before LevelInitPreEntity(). + // This can be used to access keyvalues early and without transmitting from the server. + void ParseWorldMapData( const char *pMapData ); +#endif + #ifdef MAPBASE_VSCRIPT - ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)m_iScriptLanguageClient; } + // -2 = Use server language + ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)(m_iScriptLanguageClient != -2 ? m_iScriptLanguageClient : m_iScriptLanguageServer); } #endif public: @@ -64,6 +72,7 @@ public: char m_iszChapterTitle[64]; #endif #ifdef MAPBASE_VSCRIPT + int m_iScriptLanguageServer; int m_iScriptLanguageClient; #endif diff --git a/sp/src/game/client/cdll_client_int.cpp b/sp/src/game/client/cdll_client_int.cpp index 275655f0..6fd1c711 100644 --- a/sp/src/game/client/cdll_client_int.cpp +++ b/sp/src/game/client/cdll_client_int.cpp @@ -147,6 +147,10 @@ #include "fbxsystem/fbxsystem.h" #endif +#ifdef MAPBASE_VSCRIPT +#include "vscript_client.h" +#endif + extern vgui::IInputInternal *g_InputInternal; //============================================================================= @@ -1104,6 +1108,9 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physi g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetViewEffectsRestoreBlockHandler() ); +#ifdef MAPBASE_VSCRIPT + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() ); +#endif ClientWorldFactoryInit(); @@ -1216,6 +1223,9 @@ void CHLClient::Shutdown( void ) g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetViewEffectsRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() ); +#ifdef MAPBASE_VSCRIPT + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() ); +#endif ClientVoiceMgr_Shutdown(); @@ -1635,6 +1645,10 @@ void CHLClient::LevelInitPreEntity( char const* pMapName ) tempents->LevelInit(); ResetToneMapping(1.0); +#ifdef MAPBASE + GetClientWorldEntity()->ParseWorldMapData( engine->GetMapEntitiesString() ); +#endif + IGameSystem::LevelInitPreEntityAllSystems(pMapName); #ifdef USES_ECON_ITEMS diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 937ef94a..943a22b5 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -176,7 +176,16 @@ private: HSCRIPT m_hFuncOnBind; }; +class CMaterialProxyScriptInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); +}; + +CMaterialProxyScriptInstanceHelper g_MaterialProxyScriptInstanceHelper; + BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_MaterialProxyScriptInstanceHelper ) DEFINE_SCRIPTFUNC( GetVarString, "Gets a material var's string value" ) DEFINE_SCRIPTFUNC( GetVarInt, "Gets a material var's int value" ) DEFINE_SCRIPTFUNC( GetVarFloat, "Gets a material var's float value" ) @@ -406,6 +415,19 @@ void CScriptMaterialProxy::SetVarVector( int i, const Vector &value ) } EXPOSE_INTERFACE( CScriptMaterialProxy, IMaterialProxy, "VScriptProxy" IMATERIAL_PROXY_INTERFACE_VERSION ); + +bool CMaterialProxyScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + CScriptMaterialProxy *pProxy = (CScriptMaterialProxy *)p; + V_snprintf( pBuf, bufSize, "(proxy: %s)", pProxy->GetMaterial() != NULL ? pProxy->GetMaterial()->GetName() : "" ); + return true; +} + +void *CMaterialProxyScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) +{ + // TODO: Material proxy save/restore? + return NULL; +} #endif // MAPBASE_VSCRIPT //----------------------------------------------------------------------------- @@ -616,6 +638,12 @@ bool VScriptClientInit() CGWarning( 1, CON_GROUP_VSCRIPT, "VM Did not start!\n" ); } } +#ifdef MAPBASE_VSCRIPT + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "VSCRIPT CLIENT: Not starting because language is set to 'none'\n" ); + } +#endif } else { @@ -657,9 +685,7 @@ public: virtual void LevelInitPreEntity( void ) { m_bAllowEntityCreationInScripts = true; -#ifndef MAPBASE_VSCRIPT // Now initted in C_World VScriptClientInit(); -#endif } virtual void LevelInitPostEntity( void ) diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index 60b1a873..ebab60e6 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -13,7 +13,6 @@ #include "eventqueue.h" #include "characterset.h" #include "sceneentity.h" // for exposing scene precache function -#include "isaverestore.h" #include "gamerules.h" #include "vscript_server.nut" #ifdef MAPBASE_VSCRIPT @@ -601,6 +600,12 @@ bool VScriptServerInit() CGWarning( 1, CON_GROUP_VSCRIPT, "VM Did not start!\n" ); } } +#ifdef MAPBASE_VSCRIPT + else + { + CGMsg( 0, CON_GROUP_VSCRIPT, "VSCRIPT SERVER: Not starting because language is set to 'none'\n" ); + } +#endif } else { @@ -777,172 +782,3 @@ bool IsEntityCreationAllowedInScripts( void ) return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; } - -static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2; - - -//----------------------------------------------------------------------------- - -class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler -{ -public: - CVScriptSaveRestoreBlockHandler() : - m_InstanceMap( DefLessFunc(const char *) ) - { - } - const char *GetBlockName() - { - return "VScriptServer"; - } - - //--------------------------------- - - void Save( ISave *pSave ) - { - pSave->StartBlock(); - - int temp = g_pScriptVM != NULL; - pSave->WriteInt( &temp ); - if ( g_pScriptVM ) - { - temp = g_pScriptVM->GetLanguage(); - pSave->WriteInt( &temp ); - CUtlBuffer buffer; - g_pScriptVM->WriteState( &buffer ); - temp = buffer.TellPut(); - pSave->WriteInt( &temp ); - if ( temp > 0 ) - { - pSave->WriteData( (const char *)buffer.Base(), temp ); - } - } - - pSave->EndBlock(); - } - - //--------------------------------- - - void WriteSaveHeaders( ISave *pSave ) - { - pSave->WriteShort( &VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); - } - - //--------------------------------- - - void ReadRestoreHeaders( IRestore *pRestore ) - { - // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. - short version; - pRestore->ReadShort( &version ); - m_fDoLoad = ( version == VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); - } - - //--------------------------------- - - void Restore( IRestore *pRestore, bool createPlayers ) - { - if ( !m_fDoLoad && g_pScriptVM ) - { - return; - } - CBaseEntity *pEnt = gEntList.FirstEnt(); - while ( pEnt ) - { - if ( pEnt->m_iszScriptId != NULL_STRING ) - { -#ifndef MAPBASE_VSCRIPT - g_pScriptVM->RegisterClass( pEnt->GetScriptDesc() ); -#endif - m_InstanceMap.Insert( STRING( pEnt->m_iszScriptId ), pEnt ); - } - pEnt = gEntList.NextEnt( pEnt ); - } - - pRestore->StartBlock(); - if ( pRestore->ReadInt() && pRestore->ReadInt() == g_pScriptVM->GetLanguage() ) - { - int nBytes = pRestore->ReadInt(); - if ( nBytes > 0 ) - { - CUtlBuffer buffer; - buffer.EnsureCapacity( nBytes ); - pRestore->ReadData( (char *)buffer.AccessForDirectRead( nBytes ), nBytes, 0 ); - g_pScriptVM->ReadState( &buffer ); - } - } - pRestore->EndBlock(); - } - - void PostRestore( void ) - { - for ( int i = m_InstanceMap.FirstInorder(); i != m_InstanceMap.InvalidIndex(); i = m_InstanceMap.NextInorder( i ) ) - { - CBaseEntity *pEnt = m_InstanceMap[i]; - if ( pEnt->m_hScriptInstance ) - { - ScriptVariant_t variant; - if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) - { - pEnt->m_ScriptScope.Init( variant.m_hScript, false ); - pEnt->RunPrecacheScripts(); - } - } - else - { - // Script system probably has no internal references - pEnt->m_iszScriptId = NULL_STRING; - } - } - m_InstanceMap.Purge(); - } - - - CUtlMap m_InstanceMap; - -private: - bool m_fDoLoad; -}; - -//----------------------------------------------------------------------------- - -CVScriptSaveRestoreBlockHandler g_VScriptSaveRestoreBlockHandler; - -//------------------------------------- - -ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler() -{ - return &g_VScriptSaveRestoreBlockHandler; -} - -//----------------------------------------------------------------------------- - -bool CBaseEntityScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) -{ - CBaseEntity *pEntity = (CBaseEntity *)p; - if ( pEntity->GetEntityName() != NULL_STRING ) - { - V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), STRING(pEntity->m_iClassname), STRING( pEntity->GetEntityName() ) ); - } - else - { - V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), STRING(pEntity->m_iClassname) ); - } - return true; -} - -void *CBaseEntityScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) -{ - int iEntity = g_VScriptSaveRestoreBlockHandler.m_InstanceMap.Find( pszId ); - if ( iEntity != g_VScriptSaveRestoreBlockHandler.m_InstanceMap.InvalidIndex() ) - { - CBaseEntity *pEnt = g_VScriptSaveRestoreBlockHandler.m_InstanceMap[iEntity]; - pEnt->m_hScriptInstance = hInstance; - return pEnt; - } - return NULL; -} - - -CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; - - diff --git a/sp/src/game/server/vscript_server.h b/sp/src/game/server/vscript_server.h index 6a7d44d1..2808f3eb 100644 --- a/sp/src/game/server/vscript_server.h +++ b/sp/src/game/server/vscript_server.h @@ -15,19 +15,7 @@ #pragma once #endif -class ISaveRestoreBlockHandler; - bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); -ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler(); - - -class CBaseEntityScriptInstanceHelper : public IScriptInstanceHelper -{ - bool ToString( void *p, char *pBuf, int bufSize ); - void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); -}; - -extern CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; // Only allow scripts to create entities during map initialization bool IsEntityCreationAllowedInScripts( void ); diff --git a/sp/src/game/server/world.cpp b/sp/src/game/server/world.cpp index 628a79bb..fc3d59ae 100644 --- a/sp/src/game/server/world.cpp +++ b/sp/src/game/server/world.cpp @@ -395,7 +395,7 @@ BEGIN_DATADESC( CWorld ) DEFINE_KEYFIELD( m_iszDetailSpriteMaterial, FIELD_STRING, "detailmaterial" ), #ifdef MAPBASE_VSCRIPT DEFINE_KEYFIELD( m_iScriptLanguage, FIELD_INTEGER, "vscriptlanguage" ), - DEFINE_KEYFIELD( m_iScriptLanguageClient, FIELD_INTEGER, "vscriptlanguage_client" ), + //DEFINE_KEYFIELD( m_iScriptLanguageClient, FIELD_INTEGER, "vscriptlanguage_client" ), #endif DEFINE_KEYFIELD( m_bColdWorld, FIELD_BOOLEAN, "coldworld" ), @@ -421,9 +421,6 @@ IMPLEMENT_SERVERCLASS_ST(CWorld, DT_WORLD) #ifdef MAPBASE SendPropStringT (SENDINFO(m_iszChapterTitle) ), #endif -#ifdef MAPBASE_VSCRIPT - SendPropInt (SENDINFO(m_iScriptLanguageClient), 4 ), // No SPROP_UNSIGNED to allow -1 (disabled) -#endif END_SEND_TABLE() // @@ -485,7 +482,7 @@ CWorld::CWorld( ) #ifdef MAPBASE_VSCRIPT m_iScriptLanguage = SL_NONE; - m_iScriptLanguageClient = -2; + //m_iScriptLanguageClient = -2; #endif m_bColdWorld = false; @@ -552,14 +549,6 @@ void CWorld::Spawn( void ) Precache( ); GlobalEntity_Add( "is_console", STRING(gpGlobals->mapname), ( IsConsole() ) ? GLOBAL_ON : GLOBAL_OFF ); GlobalEntity_Add( "is_pc", STRING(gpGlobals->mapname), ( !IsConsole() ) ? GLOBAL_ON : GLOBAL_OFF ); - -#ifdef MAPBASE_VSCRIPT - if (m_iScriptLanguageClient.Get() == -2) - { - // Clientside language should be regular language by default - m_iScriptLanguageClient.Set( m_iScriptLanguage ); - } -#endif } static const char *g_DefaultLightstyles[] = diff --git a/sp/src/game/server/world.h b/sp/src/game/server/world.h index 7e949450..5d75a64b 100644 --- a/sp/src/game/server/world.h +++ b/sp/src/game/server/world.h @@ -90,7 +90,7 @@ private: #ifdef MAPBASE_VSCRIPT int m_iScriptLanguage; - CNetworkVar( int, m_iScriptLanguageClient ); + //CNetworkVar( int, m_iScriptLanguageClient ); // Now entirely on client #endif // start flags diff --git a/sp/src/game/shared/mapbase/logic_script_client.cpp b/sp/src/game/shared/mapbase/logic_script_client.cpp index f7220eb8..59f673dc 100644 --- a/sp/src/game/shared/mapbase/logic_script_client.cpp +++ b/sp/src/game/shared/mapbase/logic_script_client.cpp @@ -161,6 +161,32 @@ public: BaseClass::ClientThink(); } + + void OnSave() + { + // HACKHACK: Save the next think in the VM since the VM is saved + if (m_bClientThink) + { + g_pScriptVM->SetValue( m_ScriptScope, "__c_think", GetNextThink() ); + } + + BaseClass::OnSave(); + } + + void OnRestore() + { + // HACKHACK: See OnSave() + if (m_bClientThink) + { + ScriptVariant_t flNextThink; + if (g_pScriptVM->GetValue( m_ScriptScope, "__c_think", &flNextThink )) + { + SetNextClientThink( flNextThink ); + } + } + + BaseClass::OnRestore(); + } #else void InputCallScriptFunctionClient( inputdata_t &inputdata ) { diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index ced83297..e001513e 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -757,7 +757,12 @@ public: // IGameSystem } } +#ifdef CLIENT_DLL + // On the client, OnRestore() is called before VScript is actually restored, so this has to be called manually from VScript save/restore instead + void OnVMRestore() +#else void OnRestore() +#endif { if ( g_pScriptVM ) { @@ -783,6 +788,13 @@ private: } g_ScriptSaveRestoreUtil; +#ifdef CLIENT_DLL +void VScriptSaveRestoreUtil_OnVMRestore() +{ + g_ScriptSaveRestoreUtil.OnVMRestore(); +} +#endif + CUtlMap< unsigned int, KeyValues* > CScriptSaveRestoreUtil::m_Lookup( DefLessFunc(unsigned int) ); StringHashFunctor CScriptSaveRestoreUtil::Hash; @@ -1164,6 +1176,13 @@ void CNetMsgScriptHelper::RecieveMessage( bf_read &msg ) word hash = m_MsgIn_()ReadWord(); + // Don't do anything if there's no VM here. This can happen if a message from the server goes to a VM-less client, or vice versa. + if ( !g_pScriptVM ) + { + CGWarning( 0, CON_GROUP_VSCRIPT, "CNetMsgScriptHelper: No VM on receiving side\n" ); + return; + } + ScriptVariant_t hfn; if ( g_pScriptVM->GetValue( m_Hooks, hash, &hfn ) ) { diff --git a/sp/src/game/shared/mapbase/vscript_singletons.h b/sp/src/game/shared/mapbase/vscript_singletons.h index 7e250c8b..a3b94f9b 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.h +++ b/sp/src/game/shared/mapbase/vscript_singletons.h @@ -136,4 +136,8 @@ public: extern CNetMsgScriptHelper *g_ScriptNetMsg; +#ifdef CLIENT_DLL +void VScriptSaveRestoreUtil_OnVMRestore(); +#endif + #endif diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index eb7080a5..c460a48a 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -13,6 +13,9 @@ #include "characterset.h" #include "isaverestore.h" #include "gamerules.h" +#ifdef MAPBASE_VSCRIPT +#include "mapbase/vscript_singletons.h" +#endif IScriptVM * g_pScriptVM; extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); @@ -278,3 +281,187 @@ CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) } g_pScriptVM->DumpState(); } + +//----------------------------------------------------------------------------- + +static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2; + +//----------------------------------------------------------------------------- + +class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + CVScriptSaveRestoreBlockHandler() : + m_InstanceMap( DefLessFunc(const char *) ) + { + } + const char *GetBlockName() + { +#ifdef CLIENT_DLL + return "VScriptClient"; +#else + return "VScriptServer"; +#endif + } + + //--------------------------------- + + void Save( ISave *pSave ) + { + pSave->StartBlock(); + + int temp = g_pScriptVM != NULL; + pSave->WriteInt( &temp ); + if ( g_pScriptVM ) + { + temp = g_pScriptVM->GetLanguage(); + pSave->WriteInt( &temp ); + CUtlBuffer buffer; + g_pScriptVM->WriteState( &buffer ); + temp = buffer.TellPut(); + pSave->WriteInt( &temp ); + if ( temp > 0 ) + { + pSave->WriteData( (const char *)buffer.Base(), temp ); + } + } + + pSave->EndBlock(); + } + + //--------------------------------- + + void WriteSaveHeaders( ISave *pSave ) + { + pSave->WriteShort( &VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void ReadRestoreHeaders( IRestore *pRestore ) + { + // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. + short version; + pRestore->ReadShort( &version ); + m_fDoLoad = ( version == VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void Restore( IRestore *pRestore, bool createPlayers ) + { + if ( !m_fDoLoad && g_pScriptVM ) + { + return; + } +#ifdef CLIENT_DLL + C_BaseEntity *pEnt = ClientEntityList().FirstBaseEntity(); +#else + CBaseEntity *pEnt = gEntList.FirstEnt(); +#endif + while ( pEnt ) + { + if ( pEnt->m_iszScriptId != NULL_STRING ) + { +#ifndef MAPBASE_VSCRIPT + g_pScriptVM->RegisterClass( pEnt->GetScriptDesc() ); +#endif + m_InstanceMap.Insert( STRING( pEnt->m_iszScriptId ), pEnt ); + } +#ifdef CLIENT_DLL + pEnt = ClientEntityList().NextBaseEntity( pEnt ); +#else + pEnt = gEntList.NextEnt( pEnt ); +#endif + } + + pRestore->StartBlock(); + if ( pRestore->ReadInt() && pRestore->ReadInt() == g_pScriptVM->GetLanguage() ) + { + int nBytes = pRestore->ReadInt(); + if ( nBytes > 0 ) + { + CUtlBuffer buffer; + buffer.EnsureCapacity( nBytes ); + pRestore->ReadData( (char *)buffer.AccessForDirectRead( nBytes ), nBytes, 0 ); + g_pScriptVM->ReadState( &buffer ); + } + } + pRestore->EndBlock(); + } + + void PostRestore( void ) + { + for ( int i = m_InstanceMap.FirstInorder(); i != m_InstanceMap.InvalidIndex(); i = m_InstanceMap.NextInorder( i ) ) + { + CBaseEntity *pEnt = m_InstanceMap[i]; + if ( pEnt->m_hScriptInstance ) + { + ScriptVariant_t variant; + if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) + { + pEnt->m_ScriptScope.Init( variant.m_hScript, false ); +#ifndef CLIENT_DLL + pEnt->RunPrecacheScripts(); +#endif + } + } + else + { + // Script system probably has no internal references + pEnt->m_iszScriptId = NULL_STRING; + } + } + m_InstanceMap.Purge(); + +#if defined(MAPBASE_VSCRIPT) && defined(CLIENT_DLL) + VScriptSaveRestoreUtil_OnVMRestore(); +#endif + } + + + CUtlMap m_InstanceMap; + +private: + bool m_fDoLoad; +}; + +//----------------------------------------------------------------------------- + +CVScriptSaveRestoreBlockHandler g_VScriptSaveRestoreBlockHandler; + +//------------------------------------- + +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler() +{ + return &g_VScriptSaveRestoreBlockHandler; +} + +bool CBaseEntityScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + CBaseEntity *pEntity = (CBaseEntity *)p; + if ( pEntity->GetEntityName() != NULL_STRING ) + { + V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), STRING(pEntity->m_iClassname), STRING( pEntity->GetEntityName() ) ); + } + else + { + V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), STRING(pEntity->m_iClassname) ); + } + return true; +} + +void *CBaseEntityScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) +{ + int iEntity = g_VScriptSaveRestoreBlockHandler.m_InstanceMap.Find( pszId ); + if ( iEntity != g_VScriptSaveRestoreBlockHandler.m_InstanceMap.InvalidIndex() ) + { + CBaseEntity *pEnt = g_VScriptSaveRestoreBlockHandler.m_InstanceMap[iEntity]; + pEnt->m_hScriptInstance = hInstance; + return pEnt; + } + return NULL; +} + + +CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; diff --git a/sp/src/game/shared/vscript_shared.h b/sp/src/game/shared/vscript_shared.h index a71d20b2..8bf23d54 100644 --- a/sp/src/game/shared/vscript_shared.h +++ b/sp/src/game/shared/vscript_shared.h @@ -31,6 +31,17 @@ inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = fal // Only allow scripts to create entities during map initialization bool IsEntityCreationAllowedInScripts( void ); +class ISaveRestoreBlockHandler; +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler(); + +class CBaseEntityScriptInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); +}; + +extern CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + #ifdef MAPBASE_VSCRIPT void RegisterSharedScriptConstants(); void RegisterSharedScriptFunctions();