etqw-sdk/source/game/script/Script_DLL.cpp

1683 lines
38 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
#include "Script_DLL.h"
#include "Script_ScriptObject.h"
#include "Script_Helper.h"
#include "Script_Program.h"
extern const idEventDefInternal EV_Thread_Execute;
#pragma warning( push )
#pragma warning( disable: 4312 )
class MainCoroutine : public sdDLLThread {
public:
MainCoroutine() { s_current = this; s_main = this; storedStackPointer = ( char* )0xFFFFFFFF; flags.threadDying = false; name = "main"; }
virtual void Routine() {}
virtual void SetStackEntryPoint( char* value ) { ; }
};
#pragma warning( pop )
static MainCoroutine* s_MainCoroutine;
idLinkList< sdDLLThread > sdDLLThread::s_threads;
sdDLLThread* sdDLLThread::s_current = NULL;
sdDLLThread* sdDLLThread::s_main = NULL;
char* sdDLLThread::s_errorThrown = NULL;
#if defined( ID_WIN_X86_ASM )
#define STACK_SET( value ) \
__asm { \
mov ESP, value \
}
#define STACK_GET( value ) \
__asm { \
mov value, ESP \
}
#define PUSH_REGS \
__asm { \
pusha \
}
#define POP_REGS \
__asm { \
popa \
}
#elif defined( __linux__ ) || defined( MACOS_X )
// this one is not actually used, the stack is restored at the same address so we never have to modify esp
//#define STACK_SET( value ) __asm__ __volatile__ ( "movl %0, %%esp; sub $0x1c, %%esp;" : : "m" ( value ) : )
#define STACK_SET( value ) assert( false )
// don't save/restore the locals of Coroutine_Enter, only the stack
// could have used ebp also, but then we depend on -fno-omit-frame-pointer
// if the code in Coroutine_Enter changes (or even depending on compile settings), it's very possible the offset needs to be adjusted
// (to adjust the offsets, look at the Coroutine_Enter assembly generated by the compiler)
#ifdef _DEBUG
#define STACK_GET( value ) __asm__ __volatile__ ( "movl %%esp, %0; add $0x1c, %0;" : "=m" ( value ) : : )
#else
#define STACK_GET( value ) __asm__ __volatile__ ( "movl %%esp, %0; add $0x2c, %0;" : "=m" ( value ) : : )
#endif
#define PUSH_REGS
#define POP_REGS
#else
#define STACK_SET( value )
#define STACK_GET( value )
#define PUSH_REGS
#define POP_REGS
#endif
/*
================
sdDLLTypeObject::sdDLLTypeObject
================
*/
sdDLLTypeObject::sdDLLTypeObject( const sdDLLProgram::sdDLLClassInfo* _info ) {
info = _info;
instance = info->allocator();
}
/*
================
sdDLLTypeObject::SetHandle
================
*/
void sdDLLTypeObject::SetHandle( int handle ) {
instance->__S_SetHandle( handle );
}
/*
================
sdDLLTypeObject::GetVariable
================
*/
etype_t sdDLLTypeObject::GetVariable( const char *name, byte** data ) const {
for ( const sdDLLProgram::sdDLLClassInfo* i = info; i != NULL; i = i->superClass ) {
int key = i->variablesHash.GenerateKey( name );
for ( int index = i->variablesHash.GetFirst( key ); index != -1; index = i->variablesHash.GetNext( index ) ) {
if ( idStr::Cmp( i->variables[ index ]->GetName(), name ) != 0 ) {
continue;
}
// fixme: type check
variableLookup_t lookup = i->variables[ index ]->GetLookup();
*data = ( *instance.*lookup )();
return i->variables[ index ]->GetType();
}
}
return ev_error;
}
/*
================
sdDLLFunction::sdDLLFunction
================
*/
sdDLLFunction::sdDLLFunction( sdFunctionInfo* fInfo ) {
name = fInfo->name;
function = fInfo->function;
wrapper = fInfo->wrapper;
numParms = 0;
parmSizeTotal = 0;
}
/*
================
sdDLLClassFunction::sdDLLClassFunction
================
*/
sdDLLClassFunction::sdDLLClassFunction( sdClassFunctionInfo* fInfo ) {
name = fInfo->name;
function = fInfo->function;
wrapper = fInfo->wrapper;
numParms = fInfo->numParms;
parmSizeTotal = fInfo->parmSizeTotal;
}
/*
================
sdDLLClassVariable::sdDLLClassVariable
================
*/
sdDLLClassVariable::sdDLLClassVariable( sdClassVariableInfo* vInfo ) {
name = vInfo->name;
lookup = vInfo->variable;
switch ( vInfo->type ) {
case V_FLOAT:
type = ev_float;
break;
case V_BOOLEAN:
type = ev_boolean;
break;
case V_VECTOR:
type = ev_vector;
break;
case V_OBJECT:
type = ev_object;
break;
case V_STRING:
type = ev_string;
break;
case V_WSTRING:
type = ev_wstring;
break;
}
}
/*
================
sdDLLProgram::sdDLLProgram
================
*/
sdDLLProgram::sdDLLProgram( void ) {
dllHandle = NULL;
dllInterface.Init( this );
scriptInterface = NULL;
returnValue.intValue = 0;
defaultType = NULL;
FreeScriptObjects();
}
/*
================
sdDLLProgram::CloseDLL
================
*/
void sdDLLProgram::CloseDLL( void ) {
if ( dllHandle == NULL ) {
return;
}
sys->DLL_Unload( dllHandle );
dllHandle = NULL;
}
/*
================
sdDLLProgram::Init
================
*/
bool sdDLLProgram::Init( void ) {
Shutdown();
s_MainCoroutine = new MainCoroutine();
char dllPath[ MAX_OSPATH ];
if ( gameLocal.mapMetaData->GetBool( "no_compiled_script" ) ) {
return false;
}
const char* suffix = gameLocal.mapMetaData->GetString( "compiledscript_suffix" );
fileSystem->FindDLL( va( "compiledscript%s", suffix ), dllPath, false, true );
if ( dllPath[ 0 ] == '\0' ) {
gameLocal.Warning( "sdDLLProgram::Init : Couldn't find '%s' dynamic library", dllPath );
return false;
}
dllHandle = sys->DLL_Load( dllPath, true );
if ( dllHandle == NULL ) {
gameLocal.Warning( "sdDLLProgram::Init : Couldn't load '%s' dynamic library", dllPath );
return false;
}
scriptInitFunc_t initFunc = ( scriptInitFunc_t )sys->DLL_GetProcAddress( dllHandle, "InitScripts" );
if ( initFunc == NULL ) {
CloseDLL();
gameLocal.Warning( "sdDLLProgram::Init : Couldn't Initialize Script DLL" );
return false;
}
scriptInterface = initFunc( &dllInterface );
if ( scriptInterface == NULL ) {
CloseDLL();
gameLocal.Warning( "sdDLLProgram::Init : No Script Interface Provided by DLL" );
return false;
}
int scriptVersion = scriptInterface->GetVersion();
if ( scriptVersion != COMPILED_SCRIPT_INTERFACE_VERSION ) {
gameLocal.Warning( "sdDLLProgram::Init : Version mismatch, expected %d, got %d", COMPILED_SCRIPT_INTERFACE_VERSION, scriptVersion );
return false;
}
int ourSize = sizeof( sdClassFunctionInfo );
int theirSize = scriptInterface->GetClassFunctionInfoSize();
if ( theirSize != ourSize ) {
CloseDLL();
gameLocal.Warning( "sdDLLProgram::Init : Size Mismatch on sdClassFunctionInfo" );
return false;
}
idList< const sdClassInfo* > dllClasses;
const sdClassInfo* baseClassInfo = scriptInterface->GetClassInfo();
for ( const sdClassInfo* c = baseClassInfo; c != NULL; c = c->next ) {
sdDLLClassInfo* newInfo = new sdDLLClassInfo(); // FIXME: Block Alloc
dllClasses.Alloc() = c;
newInfo->superClass = NULL;
newInfo->allocator = c->allocator;
newInfo->name = c->name;
int hashKey = classInfoHash.GenerateKey( c->name );
classInfoHash.Add( hashKey, classInfo.Num() );
classInfo.Alloc() = newInfo;
for ( sdClassFunctionInfo* fInfo = c->functionInfo; fInfo->name != NULL; fInfo++ ) {
newInfo->functionHash.Add( newInfo->functionHash.GenerateKey( fInfo->name ), newInfo->functions.Num() );
newInfo->functions.Alloc() = new sdDLLClassFunction( fInfo ); // FIXME: Block Alloc
}
for ( sdClassVariableInfo* vInfo = c->variableInfo; vInfo->name != NULL; vInfo++ ) {
newInfo->variablesHash.Add( newInfo->variablesHash.GenerateKey( vInfo->name ), newInfo->variables.Num() );
newInfo->variables.Alloc() = new sdDLLClassVariable( vInfo ); // FIXME: Block Alloc
}
}
int index = 0;
for ( const sdClassInfo* c = baseClassInfo; c != NULL; c = c->next, index++ ) {
if ( c->superClass == NULL ) {
continue;
}
bool found = false;
for ( int i = 0; i < dllClasses.Num(); i++ ) {
if ( c->superClass == dllClasses[ i ] ) {
classInfo[ index ]->superClass = classInfo[ i ];
found = true;
break;
}
}
if ( !found ) {
assert( false );
}
}
sdFunctionInfo* baseFunctionInfo = scriptInterface->GetFunctionInfo();
for ( sdFunctionInfo* f = baseFunctionInfo; f != NULL; f = f->next ) {
sdDLLFunction* newInfo = new sdDLLFunction( f );
int hashKey = functionInfoHash.GenerateKey( f->name );
functionInfoHash.Add( hashKey, functionInfo.Num() );
functionInfo.Alloc() = newInfo;
}
defaultType = FindTypeInfo( "default" );
freeThreadNums.SetNum( MAX_THREADS );
for ( int i = 0; i < MAX_THREADS; i++ ) {
freeThreadNums[ i ] = i;
}
gameLocal.Printf( "Loaded '%s'\n", dllPath );
gameLocal.Printf( " %i classes\n", dllClasses.Num() );
gameLocal.Printf( " %i global functions\n", functionInfo.Num() );
return true;
}
/*
================
sdDLLProgram::Restart
================
*/
void sdDLLProgram::Restart( void ) {
Shutdown();
Init();
}
/*
================
sdDLLProgram::Disassemble
================
*/
void sdDLLProgram::Disassemble( void ) const {
}
/*
================
sdDLLProgram::OnError
================
*/
bool sdDLLProgram::OnError( const char* text ) {
return sdDLLThread::OnError( text );
}
/*
================
sdDLLProgram::Shutdown
================
*/
void sdDLLProgram::Shutdown( void ) {
freeThreadNums.Clear();
classInfo.DeleteContents( true );
classInfoHash.Clear();
functionInfo.DeleteContents( true );
functionInfoHash.Clear();
scriptInterface = NULL;
threadAllocator.Shutdown();
for ( int i = 0; i < freeStacks.Num(); i++ ) {
freeStacks[ i ].DeleteContents( true );
}
freeStacks.Clear();
delete s_MainCoroutine;
s_MainCoroutine = NULL;
CloseDLL();
}
/*
================
sdDLLProgram::CreateThread
================
*/
sdProgramThread* sdDLLProgram::CreateThread( void ) {
if ( freeThreadNums.Num() == 0 ) {
gameLocal.Error( "sdDLLProgram::CreateThread No Free Threads" );
return NULL;
}
int count = freeThreadNums.Num();
int index = freeThreadNums[ count - 1 ];
freeThreadNums.SetNum( count - 1, false );
sdDLLThread* thread = threadAllocator.Alloc();
thread->Create( this, index );
return thread;
}
/*
================
sdDLLProgram::FindFunction
================
*/
const sdProgram::sdFunction* sdDLLProgram::FindFunction( const char* name ) {
int hashKey = functionInfoHash.GenerateKey( name );
for ( int index = functionInfoHash.GetFirst( hashKey ); index != -1; index = functionInfoHash.GetNext( index ) ) {
if ( idStr::Cmp( functionInfo[ index ]->GetName(), name ) != 0 ) {
continue;
}
return functionInfo[ index ];
}
return NULL;
}
/*
================
sdDLLProgram::FindFunction
================
*/
const sdProgram::sdFunction* sdDLLProgram::FindFunction( const char* name, const sdProgram::sdTypeObject* object ) {
const sdDLLTypeObject* t = reinterpret_cast< const sdDLLTypeObject* >( object );
const sdDLLClassInfo* info = t->GetInfo();
for ( const sdDLLClassInfo* i = info; i != NULL; i = i->superClass ) {
int hashKey = i->functionHash.GenerateKey( name );
for ( int index = i->functionHash.GetFirst( hashKey ); index != -1; index = i->functionHash.GetNext( index ) ) {
if ( idStr::Cmp( i->functions[ index ]->GetName(), name ) != 0 ) {
continue;
}
return i->functions[ index ];
}
}
return NULL;
}
/*
================
sdDLLProgram::AllocType
================
*/
sdProgram::sdTypeObject* sdDLLProgram::AllocType( sdProgram::sdTypeObject* oldType, const sdTypeInfo* type ) {
FreeType( oldType );
return new sdDLLTypeObject( reinterpret_cast< const sdDLLClassInfo* >( type ) );
}
/*
================
sdDLLProgram::FindTypeInfo
================
*/
const sdProgram::sdTypeInfo* sdDLLProgram::FindTypeInfo( const char* typeName ) {
int key = classInfoHash.GenerateKey( typeName );
for ( int index = classInfoHash.GetFirst( key ); index != -1; index = classInfoHash.GetNext( index ) ) {
if ( idStr::Cmp( classInfo[ index ]->name.c_str(), typeName ) != 0 ) {
continue;
}
return classInfo[ index ];
}
return NULL;
}
/*
================
sdDLLProgram::GetNumClasses
================
*/
int sdDLLProgram::GetNumClasses( void ) const {
return classInfo.Num();
}
/*
================
sdDLLProgram::GetClass
================
*/
const sdProgram::sdTypeInfo* sdDLLProgram::GetClass( int index ) const {
return classInfo[ index ];
}
/*
================
sdDLLProgram::AllocType
================
*/
sdProgram::sdTypeObject* sdDLLProgram::AllocType( sdProgram::sdTypeObject* oldType, const char* typeName ) {
const sdProgram::sdTypeInfo* typeInfo = FindTypeInfo( typeName );
if ( typeInfo != NULL ) {
return AllocType( oldType, typeInfo );
}
gameLocal.Error( "sdDLLProgram::AllocType Invalid Type Name: '%s'", typeName );
return NULL;
}
/*
================
sdDLLProgram::FreeType
================
*/
void sdDLLProgram::FreeType( sdProgram::sdTypeObject* oldType ) {
delete oldType;
}
/*
================
sdDLLProgram::FreeThread
================
*/
void sdDLLProgram::FreeThread( sdProgramThread* thread ) {
sdDLLThread* _thread = ( sdDLLThread* )thread;
_thread->Clear();
threadAllocator.Free( _thread );
}
/*
================
sdDLLProgram::OnThreadShutdown
================
*/
void sdDLLProgram::OnThreadShutdown( sdDLLThread* thread ) {
freeThreadNums.Alloc() = thread->GetThreadNum();
}
/*
================
sdDLLProgram::AllocStack
================
*/
char* sdDLLProgram::AllocStack( size_t size, size_t& usedSize ) {
const int index = ( int )( ( ( size + 511 ) / 512 ) - 1 );
usedSize = ( index + 1 ) * 512;
if ( index >= freeStacks.Num() ) {
freeStacks.SetNum( index + 1 );
}
int num = freeStacks[ index ].Num();
if ( num > 0 ) {
char* stack = freeStacks[ index ][ num - 1 ];
freeStacks[ index ].SetNum( num - 1, false );
return stack;
}
return new char[ usedSize ];
}
/*
================
sdDLLProgram::FreeStack
================
*/
void sdDLLProgram::FreeStack( char* stack, size_t size ) {
if ( stack == NULL ) {
return;
}
assert( ( size % 512 ) == 0 );
const int index = ( int )( ( size / 512 ) - 1 );
assert( index < freeStacks.Num() );
freeStacks[ index ].Alloc() = stack;
}
/*
================
sdDLLProgram::CreateThread
================
*/
sdProgramThread* sdDLLProgram::CreateThread( const sdScriptHelper& h ) {
const sdDLLClassFunction* function = reinterpret_cast< const sdDLLClassFunction* >( h.GetFunction() );
if ( ( function->GetParmSizeTotal() + 4 ) != h.GetSize() ) {
gameLocal.Warning( "idProgram::CreateThread Function '%s' Called With Incorrect Number Of Arguments", function->GetName() );
assert( false );
return NULL;
}
sdDLLThread* thread = reinterpret_cast< sdDLLThread* >( CreateThread() );
thread->ManualControl();
static byte buffer[ MAX_STRING_LEN * 12 ];
byte* p = buffer;
const sdScriptHelper::parmsList_t& args = h.GetArgs();
for ( int i = 0; i < args.Num(); i++ ) {
if ( args[ i ].string ) {
memcpy( p, args[ i ].string, MAX_STRING_LEN );
p += MAX_STRING_LEN;
} else {
*( int* )p = args[ i ].integer;
p += sizeof( int );
}
}
thread->call1.Init( h.GetObject(), function, buffer, this );
thread->Call( &thread->call1, false );
return thread;
}
/*
================
sdDLLProgram::KillThread
================
*/
void sdDLLProgram::KillThread( int number ) { // FIXME, these numbers need to be really unique, not just an index
sdDLLThread::KillThread( number );
}
/*
================
sdDLLProgram::KillThread
================
*/
void sdDLLProgram::KillThread( const char* name ) {
sdDLLThread::KillThread( name );
}
/*
================
sdDLLProgram::GetCurrentThread
================
*/
sdProgramThread* sdDLLProgram::GetCurrentThread( void ) {
return sdDLLThread::GetActiveThread();
}
/*
================
sdDLLProgram::ReturnStringInternal
================
*/
void sdDLLProgram::ReturnStringInternal( const char* value ) {
stringValue = value;
}
/*
================
sdDLLProgram::ReturnWStringInternal
================
*/
void sdDLLProgram::ReturnWStringInternal( const wchar_t* value ) {
wstringValue = value;
}
/*
================
sdDLLProgram::ReturnFloatInternal
================
*/
void sdDLLProgram::ReturnFloatInternal( float value ) {
returnValue.floatValue = value;
}
/*
================
sdDLLProgram::ReturnVectorInternal
================
*/
void sdDLLProgram::ReturnVectorInternal( const idVec3& value ) {
vectorValue = value;
}
/*
================
sdDLLProgram::ReturnEntityInternal
================
*/
void sdDLLProgram::ReturnEntityInternal( idEntity* value ) {
idScriptObject* obj = value ? value->GetScriptObject() : NULL;
returnValue.objectValue = obj->GetHandle();
}
/*
================
sdDLLProgram::ReturnIntegerInternal
================
*/
void sdDLLProgram::ReturnIntegerInternal( int value ) {
returnValue.intValue = value;
}
/*
================
sdDLLProgram::ReturnObjectInternal
================
*/
void sdDLLProgram::ReturnObjectInternal( idScriptObject* value ) {
returnValue.objectValue = value->GetHandle();
}
/*
================
sdDLLProgram::ListThreads
================
*/
void sdDLLProgram::ListThreads( void ) const {
sdDLLThread::ListThreads();
int count = 0;
int size = 0;
for ( int i = 0; i < freeStacks.Num(); i++ ) {
int localCount = freeStacks[ i ].Num();
size += ( ( i + 1 ) * 512 ) * localCount;
count += localCount;
}
gameLocal.Printf( "Total Free Thread Stacks: %d\n", count );
gameLocal.Printf( "Total Free Thread Stack Size: %d\n", size );
}
/*
================
sdDLLProgram::PruneThreads
================
*/
void sdDLLProgram::PruneThreads( void ) {
sdDLLThread::PruneThreads();
}
CLASS_DECLARATION( sdSysCallThread, sdDLLThread )
END_CLASS
/*
================
sdDLLThread::sdDLLThread
================
*/
sdDLLThread::sdDLLThread( void ) {
localStack = NULL;
call = NULL;
program = NULL;
threadNode.SetOwner( this );
}
/*
================
sdDLLThread::Create
================
*/
void sdDLLThread::Create( sdDLLProgram* _program, int _threadNum ) {
Init();
program = _program;
threadNum = _threadNum;
}
/*
================
sdDLLThread::Init
================
*/
void sdDLLThread::Init( void ) {
Clear();
threadNode.AddToFront( s_threads );
}
/*
================
sdDLLThread::Clear
================
*/
void sdDLLThread::Clear( void ) {
FreeStack();
if ( program != NULL ) {
program->OnThreadShutdown( this );
}
if ( call != &call1 && call != &call2 ) {
delete call;
}
if ( this == s_current ) {
s_current = s_MainCoroutine;
}
call = NULL;
pauseTime = 0;
program = NULL;
threadNum = -1;
storedStackSize = 0;
actualStackSize = 0;
storedStackPointer = NULL;
name.Clear();
callee = NULL;
caller = NULL;
flags.manualDelete = false;
flags.manualControl = false;
flags.waitFrame = false;
flags.threadDying = true;
flags.reset = false;
flags.doneProcessing = true;
flags.guiThread = false;
threadNode.Remove();
GetAutoNode().Remove();
CancelEvents( &EV_Thread_Execute );
}
/*
================
sdDLLThread::~sdDLLThread
================
*/
sdDLLThread::~sdDLLThread( void ) {
Clear();
}
/*
================
sdDLLThread::KillThread
================
*/
void sdDLLThread::KillThread( int number ) {
for ( sdDLLThread* thread = s_threads.Next(); thread != NULL; thread = thread->threadNode.Next() ) {
if ( thread->GetThreadNum() != number ) {
continue;
}
thread->EndThread();
return;
}
}
/*
================
sdDLLThread::KillThread
================
*/
void sdDLLThread::KillThread( const char* name ) {
for ( sdDLLThread* thread = s_threads.Next(); thread != NULL; thread = thread->threadNode.Next() ) {
if ( idStr::Cmp( thread->name.c_str(), name ) != 0 ) {
continue;
}
thread->EndThread();
return;
}
}
/*
================
sdDLLThread::CallFunction
================
*/
void sdDLLThread::CallFunction( const sdProgram::sdFunction* function ) {
// assert( reinterpret_cast< const sdDLLFunction* >( function )->GetNumParameters() == 0 );
call2.Init( function, NULL );
SetCall( &call2 );
}
/*
================
sdDLLThread::CallFunction
================
*/
void sdDLLThread::CallFunction( idScriptObject* obj, const sdProgram::sdFunction* function ) {
assert( reinterpret_cast< const sdDLLClassFunction* >( function )->GetNumParameters() == 0 );
call1.Init( obj, function, NULL, program );
SetCall( &call1 );
}
/*
================
sdDLLThread::DelayedStart
================
*/
void sdDLLThread::DelayedStart( int delay ) {
CancelEvents( &EV_Thread_Execute );
PostEventMS( &EV_Thread_Execute, delay );
}
/*
================
sdDLLThread::Execute
================
*/
bool sdDLLThread::Execute( void ) {
int now = GetThreadTime();
if ( flags.manualControl && ( pauseTime > now ) ) {
return false;
}
if ( flags.reset ) {
Reset();
flags.reset = false;
}
if ( !Finished() ) {
sdDLLThread::Call( this );
}
if ( s_current == s_MainCoroutine ) {
if ( s_errorThrown != NULL ) {
idStr temp = s_errorThrown;
delete s_errorThrown;
s_errorThrown = NULL;
gameLocal.Error( "%s", temp.c_str() );
}
}
if ( Finished() ) {
if ( !flags.manualDelete ) {
program->FreeThread( this );
}
return true;
} else if ( !flags.manualControl ) {
if ( flags.waitFrame ) {
flags.waitFrame = false;
if ( flags.guiThread ) {
PostGUIEventMS( &EV_Thread_Execute, NEXT_FRAME_EVENT_TIME );
} else {
PostEventMS( &EV_Thread_Execute, NEXT_FRAME_EVENT_TIME );
}
} else if ( pauseTime > now ) {
if ( flags.guiThread ) {
PostGUIEventMS( &EV_Thread_Execute, pauseTime - now );
} else {
PostEventMS( &EV_Thread_Execute, pauseTime - now );
}
}
}
return false;
}
/*
================
sdDLLThread::ManualDelete
================
*/
void sdDLLThread::ManualDelete( void ) {
flags.manualDelete = true;
}
/*
================
sdDLLThread::ManualControl
================
*/
void sdDLLThread::ManualControl( void ) {
flags.manualControl = true;
CancelEvents( &EV_Thread_Execute );
}
/*
================
sdDLLThread::EndThread
================
*/
void sdDLLThread::EndThread( void ) {
if ( flags.threadDying ) {
return;
}
flags.threadDying = true;
if ( this == GetActiveThread() ) {
Coroutine_Detach( this );
}
}
/*
================
sdDLLThread::DoneProcessing
================
*/
void sdDLLThread::DoneProcessing( void ) {
if ( flags.doneProcessing ) {
return;
}
flags.doneProcessing = true;
if ( this == GetActiveThread() ) {
Coroutine_Detach( this );
}
}
/*
================
sdDLLThread::SetName
================
*/
void sdDLLThread::SetName( const char* _name ) {
name = _name;
}
/*
================
sdDLLThread::Error
================
*/
void sdDLLThread::Error( const char* fmt, ... ) const {
va_list argptr;
char text[ 1024 ];
va_start( argptr, fmt );
vsprintf( text, fmt, argptr );
va_end( argptr );
common->Error( "Thread '%s': %s", name.c_str(), text );
}
/*
================
sdDLLThread::Warning
================
*/
void sdDLLThread::Warning( const char* fmt, ... ) const {
va_list argptr;
char text[ 1024 ];
va_start( argptr, fmt );
vsprintf( text, fmt, argptr );
va_end( argptr );
common->Warning( "Thread '%s': %s", name.c_str(), text );
}
/*
================
sdDLLThread::EnableDebugInfo
================
*/
void sdDLLThread::EnableDebugInfo( void ) {
}
/*
================
sdDLLThread::DisableDebugInfo
================
*/
void sdDLLThread::DisableDebugInfo( void ) {
}
/*
================
sdDLLThread::IsWaiting
================
*/
bool sdDLLThread::IsWaiting( void ) const {
return pauseTime > GetThreadTime();
}
/*
================
sdDLLThread::Wait
================
*/
void sdDLLThread::Wait( float time ) {
if ( time <= 0.f ) {
WaitFrame();
return;
}
pauseTime = GetThreadTime() + SEC2MS( time );
Coroutine_Detach( this );
}
/*
================
sdDLLThread::Call
================
*/
void sdDLLThread::Call( sdProcedureCall* call, bool guiThread ) {
flags.guiThread = guiThread;
SetCall( call );
}
#ifdef USE_UCONTEXT
static bool context_jump = false;
#endif
/*
================
Coroutine_Enter
================
*/
void Coroutine_Enter( sdDLLThread *self ) {
#if defined( MACOS_X ) || defined( __linux__ ) || ( defined( _WIN32 ) && !defined( _XENON ) )
PUSH_REGS;
// store a stack if we are detaching
// must not be dying off either, as that means the call here is just for exit purposes
if ( sdDLLThread::s_current->callee == NULL && !sdDLLThread::s_current->flags.doneProcessing && !sdDLLThread::s_current->flags.threadDying ) {
assert( sdDLLThread::s_current != s_MainCoroutine );
char* temp;
STACK_GET( temp );
sdDLLThread::s_current->AllocStack( temp );
}
#ifdef USE_UCONTEXT
assert( !context_jump );
int ret = getcontext( &sdDLLThread::s_current->context );
if ( ret != 0 ) {
gameLocal.Error( "getcontext %s failed", sdDLLThread::s_current->name.c_str() );
}
if ( context_jump ) {
context_jump = false;
#else
if ( setjmp( sdDLLThread::s_current->environment ) ) {
#endif
if ( sdDLLThread::s_current->IsStackSaved() ) {
char* temp;
STACK_GET( temp );
assert( sdDLLThread::s_current->storedStackPointer - sdDLLThread::s_current->storedStackSize == temp );
memcpy( temp, sdDLLThread::s_current->localStack, sdDLLThread::s_current->storedStackSize );
sdDLLThread::s_current->FreeStack();
}
POP_REGS;
return;
}
sdDLLThread::s_current = self;
self->flags.doneProcessing = false;
if ( sdDLLThread::s_current->flags.threadDying ) {
Coroutine_Detach( self );
}
if ( self->storedStackPointer == NULL ) {
sdDLLThread::s_current->FreeStack();
char* temp;
STACK_GET( temp );
self->storedStackPointer = temp;
self->Routine();
Coroutine_Detach( self );
// detach never returns
assert( false );
}
assert( sdDLLThread::s_current == self );
#ifdef USE_UCONTEXT
context_jump = true;
setcontext( &self->context );
// never returns
gameLocal.Error( "setcontext %s failed", self->name.c_str() );
#else
longjmp( self->environment, 1 );
#endif
#else
assert( false );
#endif
}
/*
===============
Coroutine_Detach
===============
*/
void Coroutine_Detach( sdDLLThread *self ) {
assert( self == sdDLLThread::s_current );
assert( sdDLLThread::s_current != s_MainCoroutine );
sdDLLThread* next = self->caller;
assert( next != NULL );
assert( next->callee == self );
next->callee = NULL;
self->caller = NULL;
Coroutine_Enter( next );
}
/*
================
sdDLLThread::Call
================
*/
void sdDLLThread::Call( sdDLLThread* next ) {
assert( next != NULL );
if ( next->caller ) {
gameLocal.Error( "Attempt to Call an attached Coroutine" );
}
s_current->callee = next;
next->caller = s_current;
while ( next->callee ) {
next = next->callee;
}
if ( next == s_current ) {
gameLocal.Error( "Attempt to Call an operating Coroutine" );
}
Coroutine_Enter( next );
}
/*
================
sdDLLThread::ListThreads
================
*/
void sdDLLThread::ListThreads( void ) {
int n = 0;
for ( sdDLLThread* thread = s_threads.Next(); thread; thread = thread->threadNode.Next(), n++ ) {
gameLocal.Printf( "%3i: %d: %-20s\n", thread->threadNum, thread->actualStackSize, thread->name.c_str() );
}
gameLocal.Printf( "%d active threads\n\n", n );
}
/*
================
sdDLLThread::PruneThreads
================
*/
void sdDLLThread::PruneThreads( void ) {
sdDLLThread* next = NULL;
for ( sdDLLThread* thread = s_threads.Next(); thread; thread = next ) {
next = thread->threadNode.Next();
if ( thread == s_MainCoroutine ) {
continue;
}
if ( thread->flags.manualDelete ) {
continue;
}
thread->program->FreeThread( thread );
}
}
/*
================
sdDLLThread::OnError
================
*/
bool sdDLLThread::OnError( const char* text ) {
assert( s_MainCoroutine != NULL );
if ( s_current == s_MainCoroutine ) {
return false;
}
const char* callstack = sys->GetCurCallStackStr( 16 );
const char* temp = va( "%s\nThread: %s\n%s", callstack, s_current->name.c_str(), text );
int len = idStr::Length( temp ) + 1;
s_errorThrown = new char[ len ];
idStr::Copynz( s_errorThrown, temp, len );
sdDLLThread* chain = s_MainCoroutine;
while ( true ) {
chain = chain->callee;
chain->EndThread();
}
return true;
}
/*
================
sdDLLThread::StackTrace
================
*/
void sdDLLThread::StackTrace( void ) const {
gameLocal.Printf( "<COMPILED SCRIPTS - STACK UNAVAILABLE>\n" );
}
/*
================
sdDLLScriptInterface::FindEvent
================
*/
const idEventDef* sdDLLScriptInterface::FindEvent( const char* name ) {
return idEventDef::FindEvent( name );
}
/*
================
sdDLLScriptInterface::AllocThread
================
*/
int sdDLLScriptInterface::AllocThread( sdProcedureCall* call ) {
sdDLLThread* thread = reinterpret_cast< sdDLLThread* >( program->CreateThread() );
thread->Call( call, false );
thread->DelayedStart( 0 );
return thread->GetThreadNum();
}
/*
================
sdDLLScriptInterface::AllocThread
================
*/
int sdDLLScriptInterface::AllocThread( sdCompiledScript_ClassBase* object, const char* name, sdProcedureCall* call ) {
assert( object != NULL );
sdDLLThread* thread = reinterpret_cast< sdDLLThread* >( program->CreateThread() );
thread->Call( call, false );
thread->DelayedStart( 0 );
idScriptObject* obj = GetScriptObject( object->__S_GetHandle() );
assert( obj != NULL );
thread->GetAutoNode().AddToEnd( obj->GetAutoThreads() );
idEntity* ent = obj->GetClass()->Cast< idEntity >();
if ( ent != NULL ) {
thread->SetName( va( "%s_%s", name, ent->name.c_str() ) );
}
return thread->GetThreadNum();
}
/*
================
sdDLLScriptInterface::AllocThread
================
*/
int sdDLLScriptInterface::AllocGuiThread( sdProcedureCall* call ) {
sdDLLThread* thread = reinterpret_cast< sdDLLThread* >( program->CreateThread() );
thread->Call( call, true );
thread->DelayedStart( 0 );
return thread->GetThreadNum();
}
/*
================
sdDLLScriptInterface::AllocThread
================
*/
int sdDLLScriptInterface::AllocGuiThread( sdCompiledScript_ClassBase* object, const char* name, sdProcedureCall* call ) {
assert( object != NULL );
sdDLLThread* thread = reinterpret_cast< sdDLLThread* >( program->CreateThread() );
thread->Call( call, true );
thread->DelayedStart( 0 );
idScriptObject* obj = GetScriptObject( object->__S_GetHandle() );
assert( obj != NULL );
thread->GetAutoNode().AddToEnd( obj->GetAutoThreads() );
idEntity* ent = obj->GetClass()->Cast< idEntity >();
if ( ent != NULL ) {
thread->SetName( va( "%s_%s", name, ent->name.c_str() ) );
}
return thread->GetThreadNum();
}
/*
================
sdDLLScriptInterface::DoAllocObject
================
*/
sdCompiledScript_ClassBase* sdDLLScriptInterface::DoAllocObject( const char* name ) {
idScriptObject* obj = program->AllocScriptObject( NULL, name );
sdScriptHelper h1;
obj->CallNonBlockingScriptEvent( obj->GetPreConstructor(), h1 );
return reinterpret_cast< sdDLLTypeObject* >( obj->GetObject() )->GetInstance();
}
/*
================
sdDLLScriptInterface::AllocObject
================
*/
sdCompiledScript_ClassBase* sdDLLScriptInterface::AllocObject( const char* name ) {
sdCompiledScript_ClassBase* obj = DoAllocObject( name );
return obj;
}
/*
================
sdDLLScriptInterface::FreeObject
================
*/
void sdDLLScriptInterface::DoFreeObject( sdCompiledScript_ClassBase* instance ) {
idScriptObject* obj = program->GetScriptObject( instance->__S_GetHandle() );
if ( obj == NULL ) {
return;
}
sdScriptHelper h1;
obj->CallNonBlockingScriptEvent( obj->GetDestructor(), h1 );
program->FreeScriptObject( obj );
return;
}
/*
================
sdDLLScriptInterface::FreeObject
================
*/
void sdDLLScriptInterface::FreeObject( sdCompiledScript_ClassBase* instance ) {
if ( instance == NULL ) {
return;
}
DoFreeObject( instance );
}
/*
================
sdDLLScriptInterface::GetObject
================
*/
sdCompiledScript_ClassBase* sdDLLScriptInterface::GetObject( int handle ) {
idScriptObject* obj = program->GetScriptObject( handle );
if ( obj == NULL ) {
return NULL;
}
return reinterpret_cast< sdDLLTypeObject* >( obj->GetObject() )->GetInstance();
}
/*
================
sdDLLScriptInterface::SysCall
================
*/
void sdDLLScriptInterface::DoSysCall( const idEventDef* event, const UINT_PTR* data ) {
if ( !sdDLLThread::GetActiveThread()->ProcessEventArgPtr( event, data ) ) {
gameLocal.Warning( "sdDLLScriptInterface::SysCall Invalid Event '%s'", event->GetName() );
}
}
/*
================
sdDLLScriptInterface::SysCall
================
*/
void sdDLLScriptInterface::SysCall( const idEventDef* event, const UINT_PTR* data ) {
DoSysCall( event, data );
}
/*
================
sdDLLScriptInterface::DoEventCall
================
*/
void sdDLLScriptInterface::DoEventCall( const idEventDef* event, sdCompiledScript_ClassBase* obj, const UINT_PTR* data ) {
idScriptObject* scriptObject = GetScriptObject( obj->__S_GetHandle() );
if ( scriptObject != NULL ) {
idClass* cls = scriptObject->GetClass();
if ( cls != NULL ) {
if ( !cls->ProcessEventArgPtr( event, data ) ) {
gameLocal.Warning( "sdDLLScriptInterface::EventCall Invalid Event '%s'", event->GetName() );
}
} else {
gameLocal.Warning( "sdDLLScriptInterface::EventCall Event Call On a NULL Object '%s'", event->GetName() );
}
}
}
/*
================
sdDLLScriptInterface::EventCall
================
*/
void sdDLLScriptInterface::EventCall( const idEventDef* event, sdCompiledScript_ClassBase* obj, const UINT_PTR* data ) {
if ( obj == NULL ) {
return;
}
DoEventCall( event, obj, data );
}
/*
================
sdDLLScriptInterface::Wait
================
*/
void sdDLLScriptInterface::Wait( float time ) {
sdDLLThread* thread = sdDLLThread::GetActiveThread();
assert( thread != NULL );
thread->Wait( time );
}
/*
================
sdDLLScriptInterface::Wait
================
*/
void sdDLLScriptInterface::WaitFrame( void ) {
sdDLLThread* thread = sdDLLThread::GetActiveThread();
assert( thread != NULL );
thread->WaitFrame();
}
/*
================
sdDLLScriptInterface::GetReturnedFloat
================
*/
float sdDLLScriptInterface::GetReturnedFloat( void ) {
return program->GetReturnedFloat();
}
/*
================
sdDLLScriptInterface::GetReturnedBoolean
================
*/
bool sdDLLScriptInterface::GetReturnedBoolean( void ) {
return program->GetReturnedInteger() != 0 ? true : false;
}
/*
================
sdDLLScriptInterface::GetReturnedObject
================
*/
sdCompiledScript_ClassBase* sdDLLScriptInterface::GetReturnedObject( void ) {
idScriptObject* obj = program->GetReturnedObject();
if ( obj == NULL ) {
return NULL;
}
return reinterpret_cast< sdDLLTypeObject* >( obj->GetObject() )->GetInstance();
}
/*
================
sdDLLScriptInterface::GetReturnedString
================
*/
const char* sdDLLScriptInterface::GetReturnedString( void ) {
return program->GetReturnedString();
}
/*
================
sdDLLScriptInterface::GetReturnedVector
================
*/
float* sdDLLScriptInterface::GetReturnedVector( void ) {
return const_cast< float* >( program->GetReturnedVector()->ToFloatPtr() );
}
/*
================
sdDLLScriptInterface::GetReturnedInteger
================
*/
int sdDLLScriptInterface::GetReturnedInteger( void ) {
return program->GetReturnedInteger();
}
/*
================
sdDLLScriptInterface::GetReturnedWString
================
*/
const wchar_t* sdDLLScriptInterface::GetReturnedWString( void ) {
return program->GetReturnedWString();
}
/*
================
sdDLLScriptInterface::GetScriptObject
================
*/
idScriptObject* sdDLLScriptInterface::GetScriptObject( int handle ) {
return program->GetScriptObject( handle );
}
/*
================
sdDLLScriptInterface::GetEntity
================
*/
idEntity* sdDLLScriptInterface::GetEntity( int handle ) {
idScriptObject* object = program->GetScriptObject( handle );
if ( object == NULL ) {
return NULL;
}
return object->GetClass()->Cast< idEntity >();
}
/*
================
sdDLLScriptInterface::ReturnString
================
*/
void sdDLLScriptInterface::ReturnString( const char* value ) {
program->ReturnStringInternal( value );
}
/*
================
sdDLLScriptInterface::ReturnWString
================
*/
void sdDLLScriptInterface::ReturnWString( const wchar_t* value ) {
program->ReturnWStringInternal( value );
}
/*
================
sdDLLScriptInterface::ReturnVector
================
*/
void sdDLLScriptInterface::ReturnVector( float* value ) {
program->ReturnVectorInternal( ( const idVec3& ) value );
}
/*
================
sdDLLScriptInterface::ReturnFloat
================
*/
void sdDLLScriptInterface::ReturnFloat( float value ) {
program->ReturnFloat( value );
}
/*
================
sdDLLScriptInterface::ReturnBoolean
================
*/
void sdDLLScriptInterface::ReturnBoolean( bool value ) {
program->ReturnIntegerInternal( value ? 1 : 0 );
}
/*
================
sdDLLScriptInterface::ReturnObject
================
*/
void sdDLLScriptInterface::ReturnObject( sdCompiledScript_ClassBase* obj ) {
program->ReturnObjectInternal( obj ? GetScriptObject( obj->__S_GetHandle() ) : NULL );
}