Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1502 lines
34 KiB

//========== Copyright � 2008, Valve Corporation, All rights reserved. ========
//
// Purpose:
//
//=============================================================================
#include <stdio.h>
#if defined(POSIX)
#include <curses.h>
#include <unistd.h>
#else
#include <io.h>
#include <conio.h>
#include <direct.h>
#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 <string>
//#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 "<unknown>";
}
}
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 "<unknown>";
}
class CLuaVM : public IScriptVM
{
lua_State *m_LuaState;
ScriptOutputFunc_t m_OutputFunc;
ScriptErrorFunc_t m_ErrorFunc;
int64 m_iUniqueIdSerialNumber;
// CUtlHashFast<SQClass *, CUtlHashFastGenericHash> 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<int>( ( 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<ScriptVariant_t, 14> 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 <time.h>
#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