//========== Copyright � 2008, Valve Corporation, All rights reserved. ======== // // Purpose: // //============================================================================= #include #if defined(POSIX) #include #include #else #include #include #include #endif #include "platform.h" #if !defined(OSX) && !defined(POSIX) extern "C" { #endif #include "lua.h" #include "lauxlib.h" #include "lualib.h" #ifdef getstr // Already defined in curses.h on linux #undef getstr #endif #include "lobject.h" #include "lstate.h" #include "ldo.h" #if !defined(OSX) && !defined(POSIX) } #endif #include "vec3.h" #include "tier1/utlmap.h" #include "datamap.h" #include "tier1/functors.h" #include "tier1/utlvector.h" #include "tier1/utlhash.h" #include "tier1/utlbuffer.h" #include "tier1/fmtstr.h" #include "tier1/convar.h" #include "mathlib/vector.h" #include "vstdlib/random.h" #include "vscript/ivscript.h" #include //#include "init_nut.h" #include "memdbgon.h" #ifdef VLUA_DEBUG_SERIALIZATION static int lastType; #endif //------------------------------------------------------------------------- // Helpers //------------------------------------------------------------------------- static const char *FieldTypeToString( int type ) { switch( type ) { case FIELD_VOID: return "void"; case FIELD_FLOAT: return "float"; case FIELD_CSTRING: return "string"; case FIELD_VECTOR: return "Vector"; case FIELD_INTEGER: return "int"; case FIELD_BOOLEAN: return "bool"; case FIELD_CHARACTER: return "char"; case FIELD_HSCRIPT: return "handle"; default: return ""; } } static const char *LUATypeToString( int nLUAType ) { switch( nLUAType ) { case LUA_TNUMBER: return "FLOAT"; case LUA_TBOOLEAN: return "BOOL"; case LUA_TSTRING: return "STRING"; case LUA_TNIL: return "NULL"; case LUA_TTABLE: return "TABLE"; case LUA_TUSERDATA: return "USERDATA"; case LUA_TTHREAD: return "THREAD"; } return ""; } class CLuaVM : public IScriptVM { lua_State *m_LuaState; ScriptOutputFunc_t m_OutputFunc; ScriptErrorFunc_t m_ErrorFunc; int64 m_iUniqueIdSerialNumber; // CUtlHashFast m_TypeMap; struct InstanceContext_t { void *pInstance; ScriptClassDesc_t *pClassDesc; char szName[ 64 ]; }; public: CLuaVM( lua_State *pState = NULL ) { m_LuaState = pState; m_iUniqueIdSerialNumber = 0; m_OutputFunc = NULL; m_ErrorFunc = NULL; // m_TypeMap.Init( 256 ); } static int PrintFunc( lua_State *pState ) { ScriptOutputFunc_t m_OutputFunc = *( ( ScriptOutputFunc_t * )lua_touserdata( pState, lua_upvalueindex( 1 ) ) ); CUtlString Output; int n = lua_gettop( pState ); /* number of arguments */ int i; lua_getglobal( pState, "towstring" ); lua_getglobal( pState, "tostring" ); for ( i=1; i <= n; i++ ) { const char *s = NULL; lua_pushvalue( pState, -1 ); /* function to be called */ lua_pushvalue( pState, i ); /* value to print */ lua_call( pState, 1, 1 ); s = lua_tostring( pState, -1 ); /* get result */ if ( s == NULL ) { return luaL_error( pState, LUA_QL( "tostring" ) " must return a string to " LUA_QL( "print" ) ); } if ( i > 1 ) { Output += "\t"; } if ( s ) { Output += s; } lua_pop( pState, 1 ); /* pop result */ } if ( m_OutputFunc ) { m_OutputFunc( Output ); } else { Msg( "%s\n", Output.Get() ); } return 0; } void HandleError( const char *pszErrorText ) { if ( m_ErrorFunc ) { m_ErrorFunc( SCRIPT_LEVEL_WARNING, pszErrorText ); } else { Msg( pszErrorText ); } } static int FatalErrorHandler( lua_State *pState ) { const char *err = lua_tostring( pState, 1 ); throw( err ); } void FatalError( const char *pszError ) { if ( m_ErrorFunc ) { m_ErrorFunc( SCRIPT_LEVEL_ERROR, pszError ); } else { Msg( pszError ); } } int GetStackSize( ) { return ( ( char * )m_LuaState->stack_last - ( char * )m_LuaState->top ) / sizeof( TValue ); } static void stackDump (lua_State *L) { int i; int top = lua_gettop(L); for (i = 1; i <= top; i++) { /* repeat for each level */ int t = lua_type(L, i); switch (t) { case LUA_TSTRING: /* strings */ printf("`%s'", lua_tostring(L, i)); break; case LUA_TBOOLEAN: /* booleans */ printf(lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: /* numbers */ printf("%g", lua_tonumber(L, i)); break; default: /* other values */ printf("%s", lua_typename(L, t)); break; } printf(" "); /* put a separator */ } printf("\n"); /* end the listing */ } //------------------------------------------------------------- // //------------------------------------------------------------- static void PushVariant( lua_State *pState, const ScriptVariant_t &value ) { switch ( value.m_type ) { case FIELD_VOID: lua_pushnil( pState ); break; case FIELD_FLOAT: lua_pushnumber( pState, value.m_float ); break; case FIELD_CSTRING: lua_pushstring( pState, value ); break; case FIELD_VECTOR: lua_newvec3( pState, value.m_pVector ); break; case FIELD_INTEGER: lua_pushinteger( pState, value.m_int ); break; case FIELD_BOOLEAN: lua_pushboolean( pState, value.m_bool ); break; case FIELD_CHARACTER: { char sz[2]; sz[0] = value.m_char; sz[1] = 0; lua_pushlstring( pState, sz, 1 ); break; } case FIELD_HSCRIPT: if ( value.m_hScript == NULL ) { lua_pushnil( pState ); } else { lua_rawgeti( pState, LUA_REGISTRYINDEX, size_cast< int > ( ( intp )value.m_hScript ) ); Assert( lua_isnil( pState, -1 ) == false ); } break; } } //------------------------------------------------------------- // //------------------------------------------------------------- static bool ConvertToVariant( int nStackIndex, lua_State *pState, ScriptVariant_t *pReturn ) { switch ( lua_type( pState, nStackIndex ) ) { case LUA_TNIL: pReturn->m_type = FIELD_VOID; break; case LUA_TNUMBER: *pReturn = ( float )lua_tonumber( pState, nStackIndex ); break; case LUA_TBOOLEAN: *pReturn = ( lua_toboolean( pState, nStackIndex ) != 0 ); break; case LUA_TSTRING: { int size = strlen( lua_tostring( pState, nStackIndex ) ) + 1; pReturn->m_type = FIELD_CSTRING; pReturn->m_pszString = new char[ size ]; memcpy( (void *)pReturn->m_pszString, lua_tostring( pState, nStackIndex ), size ); pReturn->m_flags |= SV_FREE; } break; #if 0 case OT_INSTANCE: { SQUserPointer pVector; sq_pushobject( m_hVM, object ); SQRESULT getResult = sq_getinstanceup( m_hVM, -1, &pVector, TYPETAG_VECTOR ); sq_poptop( m_hVM ); if ( getResult == SQ_OK ) { pReturn->m_type = FIELD_VECTOR; pReturn->m_pVector = new Vector( *((Vector *)pVector) ); pReturn->m_flags |= SV_FREE; break; } } // fall through #endif default: { Assert( nStackIndex == -1 ); pReturn->m_type = FIELD_HSCRIPT; pReturn->m_hScript = ( HSCRIPT )( intp )luaL_ref( pState, LUA_REGISTRYINDEX ); } } return true; } static void ReleaseVariant( lua_State *pState, ScriptVariant_t &value ) { if ( value.m_type == FIELD_HSCRIPT ) { luaL_unref( pState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )value.m_hScript ) ); } else { value.Free(); } value.m_type = FIELD_VOID; } virtual bool Init( ) { m_LuaState = luaL_newstate(); luaL_openlibs( m_LuaState ); luaopen_vec3( m_LuaState ); lua_atpanic( m_LuaState, FatalErrorHandler ); SetOutputCallback( NULL ); return true; } virtual void Shutdown() { if ( m_LuaState ) { lua_close( m_LuaState ); m_LuaState = NULL; } // m_TypeMap.Purge(); } virtual bool ConnectDebugger() { Assert( 0 ); return false; } virtual void DisconnectDebugger() { Assert( 0 ); } virtual ScriptLanguage_t GetLanguage() { return SL_LUA; } virtual const char *GetLanguageName() { return "Lua"; } virtual void AddSearchPath( const char *pszSearchPath ) { lua_getfield( m_LuaState, LUA_GLOBALSINDEX, "package" ); if ( !lua_istable( m_LuaState, -1 ) ) { Assert( 0 ); lua_pop( m_LuaState, 1 ); return; } lua_getfield( m_LuaState, -1, "path" ); if ( !lua_isstring( m_LuaState, -1 ) ) { Assert( 0 ); lua_pop( m_LuaState, 1 ); return; } CUtlString szNewPath; szNewPath = lua_tostring( m_LuaState, -1 ); szNewPath += ";"; szNewPath += pszSearchPath; szNewPath += "\\?.lua"; lua_pushstring( m_LuaState, szNewPath ); lua_setfield( m_LuaState, -3, "path" ); lua_pop( m_LuaState, 2 ); } virtual bool Frame( float simTime ) { if ( m_LuaState ) { Msg( "Garbage Collecting...\n" ); lua_gc( m_LuaState, LUA_GCCOLLECT, 0 ); } #if 0 if ( m_hDbg ) { sq_rdbg_update( m_hDbg ); if ( !m_hDbg->IsConnected() ) DisconnectDebugger(); } #endif return true; } virtual ScriptStatus_t Run( const char *pszScript, bool bWait = true ) { Assert( 0 ); return SCRIPT_ERROR; } virtual HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) { int nResult = luaL_loadbuffer( m_LuaState, pszScript, strlen( pszScript ), pszId ); if ( nResult == 0 ) { int func_ref = luaL_ref( m_LuaState, LUA_REGISTRYINDEX ); // lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, func_ref ); // lua_call( m_LuaState, 0, 0 ); return ( HSCRIPT )( intp )func_ref; } else { const char *pszErrorText = lua_tostring( m_LuaState, -1 ); HandleError( pszErrorText ); } return INVALID_HSCRIPT; } virtual void ReleaseScript( HSCRIPT hScript) { luaL_unref( m_LuaState, LUA_REGISTRYINDEX, size_cast< int > ( ( intp )hScript ) ); } virtual ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) { lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScript ) ); int nResult = lua_pcall( m_LuaState, 0, LUA_MULTRET, 0 ); switch( nResult ) { case LUA_ERRRUN: { const char *pszErrorText = lua_tostring( m_LuaState, -1 ); HandleError( pszErrorText ); return SCRIPT_ERROR; } case LUA_ERRMEM: { const char *pszErrorText = lua_tostring( m_LuaState, -1 ); HandleError( pszErrorText ); return SCRIPT_ERROR; } case LUA_ERRERR: { const char *pszErrorText = lua_tostring( m_LuaState, -1 ); HandleError( pszErrorText ); return SCRIPT_ERROR; } } return SCRIPT_DONE; } virtual ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) { Assert( 0 ); return SCRIPT_ERROR; } // stack good virtual HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) { return NULL; } virtual void ReleaseScope( HSCRIPT hScript ) { } // stack good virtual HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) { if ( hScope ) { lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); if ( lua_isnil( m_LuaState, -1 ) ) { Assert( 0 ); lua_pop( m_LuaState, 1 ); return NULL; } lua_getfield( m_LuaState, -1, pszFunction ); if ( lua_isnil( m_LuaState, -1 ) || !lua_isfunction( m_LuaState, -1 ) ) { lua_pop( m_LuaState, 2 ); return NULL; } int func_ref = luaL_ref( m_LuaState, LUA_REGISTRYINDEX ); lua_pop( m_LuaState, 1 ); return ( HSCRIPT )( intp )func_ref; } else { lua_getfield( m_LuaState, LUA_GLOBALSINDEX, pszFunction ); if ( lua_isnil( m_LuaState, -1 ) || !lua_isfunction( m_LuaState, -1 ) ) { lua_pop( m_LuaState, 1 ); return NULL; } int func_ref = luaL_ref( m_LuaState, LUA_REGISTRYINDEX ); return ( HSCRIPT )( intp )func_ref; } } // stack good virtual void ReleaseFunction( HSCRIPT hScript ) { luaL_unref( m_LuaState, LUA_REGISTRYINDEX, size_cast( ( intp )hScript ) ); } // stack good virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) { if ( hScope == INVALID_HSCRIPT ) { DevWarning( "ExecuteFunction: Invalid scope handed to script VM\n" ); return SCRIPT_ERROR; } #if 0 if ( m_hDbg ) { extern bool g_bSqDbgTerminateScript; if ( g_bSqDbgTerminateScript ) { DisconnectDebugger(); g_bSqDbgTerminateScript = false; } } #endif Assert( bWait ); try { if ( hFunction ) { int nStackSize = GetStackSize(); lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hFunction ) ); int nTopStack = lua_gettop( m_LuaState ); if ( hScope ) { lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); } for ( int i = 0; i < nArgs; i++ ) { PushVariant( m_LuaState, pArgs[i] ); } lua_call( m_LuaState, lua_gettop( m_LuaState ) - nTopStack, ( pReturn ? 1 : 0 ) ); if ( pReturn ) { ConvertToVariant( -1, m_LuaState, pReturn ); } lua_pop( m_LuaState, nStackSize - GetStackSize() ); return SCRIPT_DONE; } if ( pReturn ) { pReturn->m_type = FIELD_VOID; } } catch( const char *pszString ) { FatalError( pszString ); } return SCRIPT_ERROR; } // stack good static int TranslateCall( lua_State *pState ) { int nActualParams = lua_gettop( pState ); ScriptFunctionBinding_t *pVMScriptFunction = ( ScriptFunctionBinding_t * )lua_touserdata( pState, lua_upvalueindex( 1 ) ); int nFormalParams = pVMScriptFunction->m_desc.m_Parameters.Count(); CUtlVectorFixed params; ScriptVariant_t returnValue; bool bCallFree = false; params.SetSize( nFormalParams ); void *pObject = NULL; if ( nActualParams ) { int nOffset = 1; if ( ( pVMScriptFunction->m_flags & SF_MEMBER_FUNC ) ) { InstanceContext_t *pInstanceContext = ( InstanceContext_t * )lua_touserdata( pState, nOffset ); pObject = pInstanceContext->pInstance; if ( pInstanceContext->pClassDesc->pHelper ) { pObject = pInstanceContext->pClassDesc->pHelper->GetProxied( pObject ); } if ( !pObject ) { // sq_throwerror( hVM, "Accessed null instance" ); return SCRIPT_ERROR; } nOffset++; nActualParams--; } int iLimit = ( nActualParams < nFormalParams ? nActualParams : nFormalParams ); ScriptDataType_t *pCurParamType = pVMScriptFunction->m_desc.m_Parameters.Base(); for ( int i = 0; i < iLimit; i++, pCurParamType++ ) { switch ( *pCurParamType ) { case FIELD_FLOAT: params[ i ] = ( float )lua_tonumber( pState, i + nOffset ); break; case FIELD_CSTRING: params[ i ] = lua_tostring( pState, i + nOffset ); break; case FIELD_VECTOR: { params[ i ] = lua_getvec3( pState, i + nOffset ); break; } case FIELD_INTEGER: params[ i ] = ( int )lua_tonumber( pState, i + nOffset ); break; case FIELD_BOOLEAN: params[ i ] = ( lua_toboolean( pState, i + nOffset ) != 0 ); break; case FIELD_CHARACTER: params[ i ] = lua_tostring( pState, i + nOffset )[0]; break; case FIELD_HSCRIPT: { lua_pushvalue( pState, i + nOffset ); params[ i ] = ( HSCRIPT )( intp )luaL_ref( pState, LUA_REGISTRYINDEX ); // params[ i ].m_flags |= SV_FREE; bCallFree = true; break; } default: break; } } } (*pVMScriptFunction->m_pfnBinding)( pVMScriptFunction->m_pFunction, pObject, params.Base(), params.Count(), ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID ) ? &returnValue : NULL ); if ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID ) { PushVariant( pState, returnValue ); } if ( bCallFree ) { for ( int i = 0; i < params.Count(); i++ ) { ReleaseVariant( pState, params[ i ] ); // params[i].Free(); } } return ( ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID ) ? 1 : 0 ) ; } //------------------------------------------------------------- // //------------------------------------------------------------- // stack good void RegisterFunctionGuts( ScriptFunctionBinding_t *pScriptFunction, HSCRIPT pOwningClass = NULL ) { char szTypeMask[ 64 ]; if ( pScriptFunction->m_desc.m_Parameters.Count() > ARRAYSIZE(szTypeMask) - 1 ) { AssertMsg1( 0, "Too many agruments for script function %s\n", pScriptFunction->m_desc.m_pszFunction ); return; } szTypeMask[0] = '.'; char *pCurrent = &szTypeMask[1]; for ( int i = 0; i < pScriptFunction->m_desc.m_Parameters.Count(); i++, pCurrent++ ) { switch ( pScriptFunction->m_desc.m_Parameters[i] ) { case FIELD_CSTRING: *pCurrent = 's'; break; case FIELD_FLOAT: case FIELD_INTEGER: *pCurrent = 'n'; break; case FIELD_BOOLEAN: *pCurrent = 'b'; break; case FIELD_VECTOR: *pCurrent = 'x'; break; case FIELD_HSCRIPT: *pCurrent = '.'; break; case FIELD_CHARACTER: default: *pCurrent = FIELD_VOID; AssertMsg( 0 , "Not supported" ); break; } } int nStackIndex = LUA_GLOBALSINDEX; if ( pOwningClass ) { lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )pOwningClass ) ); if ( !lua_isnil( m_LuaState, -1 ) ) { nStackIndex = lua_gettop( m_LuaState ); } } Assert( pCurrent - szTypeMask < ARRAYSIZE(szTypeMask) - 1 ); *pCurrent = 0; lua_pushstring( m_LuaState, pScriptFunction->m_desc.m_pszScriptName ); lua_pushlightuserdata( m_LuaState, pScriptFunction ); lua_pushcclosure( m_LuaState, &TranslateCall, 1 ); lua_settable( m_LuaState, nStackIndex ); if ( pOwningClass ) { lua_pop( m_LuaState, 1 ); } if ( nStackIndex == LUA_GLOBALSINDEX ) { Msg( "VLua: Registered GLOBAL function %s\n", pScriptFunction->m_desc.m_pszScriptName ); } else { Msg( "VLua: Registered TABLE function %s\n", pScriptFunction->m_desc.m_pszScriptName ); } #if 0 if ( developer.GetInt() ) { const char *pszHide = SCRIPT_HIDE; if ( !pScriptFunction->m_desc.m_pszDescription || *pScriptFunction->m_desc.m_pszDescription != *pszHide ) { std::string name; std::string signature; if ( pClassDesc ) { name += pClassDesc->m_pszScriptName; name += "::"; } name += pScriptFunction->m_desc.m_pszScriptName; signature += FieldTypeToString( pScriptFunction->m_desc.m_ReturnType ); signature += ' '; signature += name; signature += '('; for ( int i = 0; i < pScriptFunction->m_desc.m_Parameters.Count(); i++ ) { if ( i != 0 ) { signature += ", "; } signature+= FieldTypeToString( pScriptFunction->m_desc.m_Parameters[i] ); } signature += ')'; sq_pushobject( m_hVM, LookupObject( "RegisterFunctionDocumentation", NULL, false ) ); sq_pushroottable( m_hVM ); sq_pushobject( m_hVM, hFunction ); sq_pushstring( m_hVM, name.c_str(), name.length() ); sq_pushstring( m_hVM, signature.c_str(), signature.length() ); sq_pushstring( m_hVM, pScriptFunction->m_desc.m_pszDescription, -1 ); sq_call( m_hVM, 5, false, /*false*/ true ); sq_pop( m_hVM, 1 ); } } #endif } virtual void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) { RegisterFunctionGuts( pScriptFunction ); } static int custom_index( lua_State *pState ) { InstanceContext_t *pInstanceContext = ( ( InstanceContext_t * )lua_touserdata( pState, 1 ) ); ScriptClassDesc_t *pVMScriptFunction = pInstanceContext->pClassDesc; const char *pszKey = luaL_checkstring( pState, 2 ); while( pVMScriptFunction ) { lua_getfield( pState, LUA_REGISTRYINDEX, pVMScriptFunction->m_pszClassname ); if ( lua_isnil( pState, -1 ) ) { break; } lua_pushstring( pState, pszKey ); lua_rawget( pState, -2 ); if ( lua_isnil( pState, -1 ) ) { lua_pop( pState, 2 ); pVMScriptFunction = pVMScriptFunction->m_pBaseDesc; } else { break; } } return 1; } /* static int custom_gc( lua_State *pState ) { InstanceContext_t *pInstanceContext = ( ( InstanceContext_t * )lua_touserdata( pState, 1 ) ); return 0; } */ // stack good virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) { if ( luaL_newmetatable( m_LuaState, pClassDesc->m_pszScriptName ) == 0 ) { lua_pop( m_LuaState, 1 ); return true; } // lua_pushcclosure( m_LuaState, custom_gc, 0 ); // lua_setfield( m_LuaState, -2, "__gc" ); lua_pushcclosure( m_LuaState, custom_index, 0 ); lua_setfield( m_LuaState, -2, "__index" ); HSCRIPT ClassReference = ( HSCRIPT )( intp )luaL_ref( m_LuaState, LUA_REGISTRYINDEX ); for ( int i = 0; i < pClassDesc->m_FunctionBindings.Count(); i++ ) { RegisterFunctionGuts( &pClassDesc->m_FunctionBindings[i], ClassReference ); } if ( pClassDesc->m_pBaseDesc ) { RegisterClass( pClassDesc->m_pBaseDesc ); } luaL_unref( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )ClassReference ) ); return true; } // stack good virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pClassDesc, void *pInstance ) { HSCRIPT Instance = NULL; if ( !RegisterClass( pClassDesc ) ) { return NULL; } InstanceContext_t *pInstanceContext = ( InstanceContext_t * )lua_newuserdata( m_LuaState, sizeof( InstanceContext_t ) ); pInstanceContext->pInstance = pInstance; pInstanceContext->pClassDesc = pClassDesc; luaL_getmetatable( m_LuaState, pClassDesc->m_pszScriptName ); lua_setmetatable( m_LuaState, -2 ); Instance = ( HSCRIPT )( intp )luaL_ref( m_LuaState, LUA_REGISTRYINDEX ); return Instance; } // stack good virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) { #if 0 TValue *pValue = ( TValue * )hInstance; if ( !hInstance ) { Assert( 0 ); return; } Assert( ttislightuserdata( pValue ) ); InstanceContext_t *pInstanceContext = ( InstanceContext_t * )( pValue->value.p ); if ( pInstanceContext == NULL ) { Assert( 0 ); return; } lua_getfield( m_LuaState, LUA_REGISTRYINDEX, pInstanceContext->szName ); //-- strcpy( pInstanceContext->szName, pszId ); lua_setfield( m_LuaState, LUA_REGISTRYINDEX, pInstanceContext->szName ); //-- #endif Assert( 0 ); } // stack good virtual void RemoveInstance( HSCRIPT hInstance ) { #if 0 TValue *pValue = ( TValue * )hInstance; if ( !pValue ) { Assert( 0 ); return; } Assert( ttislightuserdata( pValue ) ); InstanceContext_t *pInstanceContext = ( InstanceContext_t * )( pValue->value.p ); lua_pushnil( m_LuaState ); lua_setfield( m_LuaState, LUA_REGISTRYINDEX, pInstanceContext->szName ); // lua_pushnil( m_LuaState ); //-- // lua_setfield( m_LuaState, LUA_GLOBALSINDEX, pInstanceContext->szName ); //-- delete pInstanceContext; delete pValue; #endif Assert( 0 ); } virtual void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType = NULL ) { Assert( 0 ); return NULL; } virtual bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) { Assert( V_strlen(pszRoot) + 32 <= nBufSize ); Q_snprintf( pBuf, nBufSize, "_%x%I64x_%s", RandomInt(0, 0xfff), m_iUniqueIdSerialNumber++, pszRoot ); // random to limit key compare when serial number gets large return true; } // stack good virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) { Assert( hScope == NULL ); lua_getfield( m_LuaState, LUA_GLOBALSINDEX, pszKey ); bool bResult = ( lua_isnil( m_LuaState, -1 ) == false ); lua_pop( m_LuaState, 1 ); return bResult; } virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) { // Not supported yet. Assert(0); return false; } // stack good virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) { if ( hScope ) { // Msg( "SetValue: SCOPE %s\n", pszKey ); lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); lua_pushstring( m_LuaState, pszKey ); PushVariant( m_LuaState, value ); lua_settable( m_LuaState, -3 ); lua_pop( m_LuaState, 1 ); } else { // Msg( "SetValue: NORMAL %s\n", pszKey ); lua_pushstring( m_LuaState, pszKey ); PushVariant( m_LuaState, value ); lua_settable( m_LuaState, LUA_GLOBALSINDEX ); } return true; } virtual bool SetValue( HSCRIPT hScope, int nIndex, const ScriptVariant_t &value ) { Assert( hScope ); lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); lua_pushinteger( m_LuaState, nIndex ); PushVariant( m_LuaState, value ); lua_settable( m_LuaState, -3 ); return true; } virtual void CreateTable( ScriptVariant_t &Result ) { lua_newtable( m_LuaState ); ConvertToVariant( -1, m_LuaState, &Result ); } // stack good virtual int GetNumTableEntries( HSCRIPT hScope ) { // Should this also check for 0? if ( hScope == INVALID_HSCRIPT ) return 0; int nCount = 0; lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); // int count = lua_objlen( m_LuaState, -1 ); lua_pushnil( m_LuaState ); /* first key */ while ( lua_next( m_LuaState, -2 ) != 0 ) { /* key is at index -2 and value at index -1 */ // Msg ("%s - %s\n", lua_typename(m_LuaState, lua_type(m_LuaState, -2)), lua_typename(m_LuaState, lua_type(m_LuaState, -1))); nCount++; lua_pop( m_LuaState, 1 ); /* removes value; keeps key for next iteration */ } lua_pop( m_LuaState, 1 ); /* removes value; keeps key for next iteration */ return nCount; } // stack good virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) { int nCount = 0; int nStackSize = GetStackSize(); lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); // int count = lua_objlen( m_LuaState, -1 ); lua_pushnil( m_LuaState ); /* first key */ while ( lua_next( m_LuaState, -2 ) != 0 && nCount < nIterator ) { nCount++; lua_pop( m_LuaState, 1 ); /* removes value; keeps key for next iteration */ } // Msg ("%s - %s\n", lua_typename(m_LuaState, lua_type(m_LuaState, -2)), lua_typename(m_LuaState, lua_type(m_LuaState, -1))); ConvertToVariant( -2, m_LuaState, pKey ); ConvertToVariant( -1, m_LuaState, pValue ); lua_pop( m_LuaState, 3 ); /* removes value; keeps key for next iteration */ lua_pop( m_LuaState, nStackSize - GetStackSize() ); return nCount + 1; } virtual bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) { int nStackSize = GetStackSize(); if ( hScope ) { lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); lua_getfield( m_LuaState, -1, pszKey ); if ( lua_isnil( m_LuaState, -1 ) ) { lua_pop( m_LuaState, nStackSize - GetStackSize() ); return false; } } else { lua_getfield( m_LuaState, LUA_GLOBALSINDEX, pszKey ); if ( lua_isnil( m_LuaState, -1 ) ) { lua_pop( m_LuaState, nStackSize - GetStackSize() ); return false; } } ConvertToVariant( -1, m_LuaState, pValue ); lua_pop( m_LuaState, nStackSize - GetStackSize() ); return true; } virtual bool GetValue( HSCRIPT hScope, int nIndex, ScriptVariant_t *pValue ) { if ( hScope ) { int nStackSize = GetStackSize(); lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, size_cast< int >( ( intp )hScope ) ); lua_rawgeti( m_LuaState, -1, nIndex ); if ( lua_isnil( m_LuaState, -1 ) ) { lua_pop( m_LuaState, nStackSize - GetStackSize() ); return false; } ConvertToVariant( -1, m_LuaState, pValue ); lua_pop( m_LuaState, nStackSize - GetStackSize() ); return true; } return false; } virtual void ReleaseValue( ScriptVariant_t &value ) { ReleaseVariant( m_LuaState, value ); } virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) { Assert( 0 ); return false; } virtual void WriteState( CUtlBuffer *pBuffer ) { Assert( 0 ); } virtual void ReadState( CUtlBuffer *pBuffer ) { Assert( 0 ); } virtual void RemoveOrphanInstances() { Assert( 0 ); } virtual void DumpState() { Assert( 0 ); } //------------------------------------------------------------- // //------------------------------------------------------------- virtual bool RaiseException( const char *pszExceptionText ) { return true; } virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) { m_OutputFunc = pFunc; lua_pushstring( m_LuaState, "print" ); ScriptOutputFunc_t *pOutputCallback = ( ScriptOutputFunc_t * )lua_newuserdata( m_LuaState, sizeof( ScriptOutputFunc_t ) ); *pOutputCallback = m_OutputFunc; lua_pushcclosure( m_LuaState, PrintFunc, 1 ); lua_settable( m_LuaState, LUA_GLOBALSINDEX ); } virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) { m_ErrorFunc = pFunc; } }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- IScriptVM *ScriptCreateLuaVM() { return new CLuaVM; } void ScriptDestroyLuaVM( IScriptVM *pVM ) { CLuaVM *pLuaVM = assert_cast< CLuaVM * >( pVM ); delete pLuaVM; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- #ifdef VLUA_TEST CLuaVM g_LuaVM; IScriptVM *g_pScriptVM = &g_LuaVM; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- #include #include "fasttimer.h" static void FromScript_AddBehavior( const char *pBehaviorName, HSCRIPT hTable ) { ScriptVariant_t KeyVariant, ValueVariant; Msg( "Behavior: %s\n", pBehaviorName ); int nInterator = 0; int index = g_pScriptVM->GetNumTableEntries( hTable ); for( int i = 0; i < index; i++ ) { nInterator = g_pScriptVM->GetKeyValue( hTable, nInterator, &KeyVariant, &ValueVariant ); Msg( " %d: %s / %s\n", i, KeyVariant.m_pszString, ValueVariant.m_pszString ); g_pScriptVM->ReleaseValue( KeyVariant ); g_pScriptVM->ReleaseValue( ValueVariant ); } } static Vector MyVectorAdd( Vector A, Vector B ) { return A + B; } void TestOutput( const char *pszText ) { Msg( "%s\n", pszText ); } bool TestError( const char *pszText ) { Msg( "%s\n", pszText ); return true; } class CMyClass { public: bool Foo( int ); void Bar( HSCRIPT TableA, HSCRIPT TableB ); float FooBar( int, const char * ); float OverlyTechnicalName( bool ); }; bool CMyClass::Foo( int test ) { return true; } void CMyClass::Bar( HSCRIPT TableA, HSCRIPT TableB ) { ScriptVariant_t MyValue; // g_pScriptVM->CreateTable( MyTable ); MyValue = 10; g_pScriptVM->SetValue( TableA, 1, MyValue ); MyValue = 20; g_pScriptVM->SetValue( TableA, 2, MyValue ); MyValue = 30; g_pScriptVM->SetValue( TableA, 3, MyValue ); MyValue = 100; g_pScriptVM->SetValue( TableB, 1, MyValue ); MyValue = 200; g_pScriptVM->SetValue( TableB, 2, MyValue ); MyValue = 300; g_pScriptVM->SetValue( TableB, 3, MyValue ); // return MyTable; } float CMyClass::FooBar( int test1, const char *test2 ) { return 2.34f; } float CMyClass::OverlyTechnicalName( bool test ) { return 4.56f; } BEGIN_SCRIPTDESC_ROOT_NAMED( CMyClass , "CMyClass", SCRIPT_SINGLETON "" ) DEFINE_SCRIPTFUNC( Foo, "" ) DEFINE_SCRIPTFUNC( Bar, "" ) DEFINE_SCRIPTFUNC( FooBar, "" ) DEFINE_SCRIPTFUNC_NAMED( OverlyTechnicalName, "SimpleMemberName", "" ) END_SCRIPTDESC(); class CMyDerivedClass : public CMyClass { public: float DerivedFunc() const; }; BEGIN_SCRIPTDESC( CMyDerivedClass, CMyClass, SCRIPT_SINGLETON "" ) DEFINE_SCRIPTFUNC( DerivedFunc, "" ) END_SCRIPTDESC(); float CMyDerivedClass::DerivedFunc() const { return 8.91f; } CMyDerivedClass derivedInstance; void AnotherFunction() { // Manual class exposure g_pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) ); // Auto registration by instance g_pScriptVM->RegisterInstance( &derivedInstance, "theInstance" ); } int main( int argc, const char **argv) { if ( argc < 2 ) { printf( "No script specified" ); return 1; } g_pScriptVM->Init(); g_pScriptVM->SetOutputCallback( TestOutput ); g_pScriptVM->SetErrorCallback( TestError ); AnotherFunction(); CCycleCount count; count.Sample(); RandomSeed( time( NULL ) ^ count.GetMicroseconds() ); ScriptRegisterFunction( g_pScriptVM, RandomFloat, "" ); ScriptRegisterFunction( g_pScriptVM, RandomInt, "" ); ScriptRegisterFunction( g_pScriptVM, FromScript_AddBehavior, "" ); ScriptRegisterFunction( g_pScriptVM, MyVectorAdd, "" ); if ( argc == 3 && *argv[2] == 'd' ) { g_pScriptVM->ConnectDebugger(); } int key; CScriptScope scope; scope.Init( "TestScope" ); do { const char *pszScript = argv[1]; FILE *hFile = fopen( pszScript, "rb" ); if ( !hFile ) { printf( "\"%s\" not found.\n", pszScript ); return 1; } int nFileLen = _filelength( _fileno( hFile ) ); char *pBuf = new char[nFileLen + 1]; fread( pBuf, 1, nFileLen, hFile ); pBuf[nFileLen] = 0; fclose( hFile ); if (1) { printf( "Executing script \"%s\"\n----------------------------------------\n", pszScript ); HSCRIPT hScript = g_pScriptVM->CompileScript( pBuf, ( strrchr( pszScript, '\\' ) ? strrchr( pszScript, '\\' ) + 1 : pszScript ) ); if ( hScript ) { if ( scope.Run( hScript ) != SCRIPT_ERROR ) { printf( "----------------------------------------\n" ); printf("Script complete. Press q to exit, m to dump memory usage, enter to run again.\n"); } else { printf( "----------------------------------------\n" ); printf("Script execution error. Press q to exit, m to dump memory usage, enter to run again.\n"); } g_pScriptVM->ReleaseScript( hScript ); } else { printf( "----------------------------------------\n" ); printf("Script failed to compile. Press q to exit, m to dump memory usage, enter to run again.\n"); } } key = _getch(); // Keypress before exit if ( key == 'm' ) { Msg( "%d\n", g_pMemAlloc->GetSize( NULL ) ); } delete pBuf; } while ( key != 'q' ); scope.Term(); // g_pScriptVM->DisconnectDebugger(); g_pScriptVM->Shutdown(); return 0; } #endif // add a check stack auto class to each function