2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright ( c ) 2009 - 2016 , The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-08 13:28:41 +00:00
*/
2012-05-14 20:07:33 +00:00
// Miscellaneous script engine bits
2009-05-08 13:28:41 +00:00
2016-04-03 18:07:56 +00:00
# include "C4Include.h"
# include "script/C4Aul.h"
2017-02-13 13:53:17 +00:00
2017-04-30 08:49:09 +00:00
# include "c4group/C4Components.h"
# include "c4group/C4LangStringTable.h"
2016-04-03 18:07:56 +00:00
# include "script/C4AulDebug.h"
2017-04-30 08:49:09 +00:00
# include "script/C4AulExec.h"
2016-04-28 01:00:04 +00:00
# include "script/C4Effect.h"
2016-10-20 16:30:05 +00:00
# include "script/C4ScriptHost.h"
2009-05-08 13:28:41 +00:00
2017-02-13 13:53:17 +00:00
const char * C4AulWarningMessages [ ] = {
# define DIAG(id, text, enabled) text,
# include "C4AulWarnings.h"
# undef DIAG
nullptr
} ;
const char * C4AulWarningIDs [ ] = {
# define DIAG(id, text, enabled) #id,
# include "C4AulWarnings.h"
# undef DIAG
nullptr
} ;
static_assert ( std : : extent < decltype ( C4AulWarningMessages ) , 0 > : : value - 1 = = static_cast < size_t > ( C4AulWarningId : : WarningCount ) , " Warning message count doesn't match warning count " ) ;
static_assert ( std : : extent < decltype ( C4AulWarningIDs ) , 0 > : : value - 1 = = static_cast < size_t > ( C4AulWarningId : : WarningCount ) , " Warning ID count doesn't match warning count " ) ;
2016-07-25 12:57:54 +00:00
static class DefaultErrorHandler : public C4AulErrorHandler
2010-03-28 18:58:01 +00:00
{
2016-07-25 12:57:54 +00:00
public :
void OnError ( const char * msg ) override
{
DebugLogF ( " ERROR: %s " , msg ) ;
2016-10-31 10:15:19 +00:00
+ + : : ScriptEngine . errCnt ;
2016-07-25 12:57:54 +00:00
}
void OnWarning ( const char * msg ) override
{
DebugLogF ( " WARNING: %s " , msg ) ;
2016-10-31 10:15:19 +00:00
+ + : : ScriptEngine . warnCnt ;
2016-07-25 12:57:54 +00:00
}
} DefaultErrorHandler ;
2009-05-08 13:28:41 +00:00
2016-01-22 23:57:30 +00:00
const char * C4AulError : : what ( ) const noexcept
{
return sMessage ? sMessage . getData ( ) : " (unknown error) " ;
}
2009-05-08 13:28:41 +00:00
/*--- C4AulScriptEngine ---*/
C4AulScriptEngine : : C4AulScriptEngine ( ) :
2016-11-02 23:58:02 +00:00
C4PropListStaticMember ( nullptr , nullptr , : : Strings . RegString ( " Global " ) ) ,
2017-05-07 11:50:00 +00:00
ErrorHandler ( & DefaultErrorHandler )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
GlobalNamedNames . Reset ( ) ;
GlobalNamed . Reset ( ) ;
GlobalNamed . SetNameList ( & GlobalNamedNames ) ;
GlobalConstNames . Reset ( ) ;
GlobalConsts . Reset ( ) ;
GlobalConsts . SetNameList ( & GlobalConstNames ) ;
2016-11-02 23:58:02 +00:00
Child0 = ChildL = nullptr ;
2016-01-03 19:44:05 +00:00
RegisterGlobalConstant ( " Global " , C4VPropList ( this ) ) ;
2011-10-04 20:12:45 +00:00
}
2009-05-08 13:28:41 +00:00
2012-07-25 23:27:52 +00:00
C4AulScriptEngine : : ~ C4AulScriptEngine ( )
{
Clear ( ) ;
}
2009-05-08 13:28:41 +00:00
void C4AulScriptEngine : : Clear ( )
2010-03-28 18:58:01 +00:00
{
2010-02-23 18:29:26 +00:00
# ifndef NOAULDEBUG
2009-04-21 21:44:56 +00:00
// stop debugger
2011-03-02 23:58:43 +00:00
delete C4AulDebug : : GetDebugger ( ) ;
2010-02-23 18:29:26 +00:00
# endif
2012-01-25 03:15:39 +00:00
while ( Child0 )
if ( Child0 - > Delete ( ) ) delete Child0 ;
else Child0 - > Unreg ( ) ;
2009-05-08 13:28:41 +00:00
// clear own stuff
2016-01-03 19:44:05 +00:00
C4PropListStaticMember : : Clear ( ) ;
2009-05-08 13:28:41 +00:00
// reset values
2012-01-25 03:15:53 +00:00
warnCnt = errCnt = lineCnt = 0 ;
2009-05-08 13:28:41 +00:00
// resetting name lists will reset all data lists, too
// except not...
GlobalNamedNames . Reset ( ) ;
GlobalConstNames . Reset ( ) ;
GlobalConsts . Reset ( ) ;
GlobalConsts . SetNameList ( & GlobalConstNames ) ;
2016-01-03 19:44:05 +00:00
RegisterGlobalConstant ( " Global " , C4VPropList ( this ) ) ;
2009-05-08 13:28:41 +00:00
GlobalNamed . Reset ( ) ;
GlobalNamed . SetNameList ( & GlobalNamedNames ) ;
2016-11-02 23:58:02 +00:00
delete pGlobalEffects ; pGlobalEffects = nullptr ;
2013-12-22 22:47:40 +00:00
UserFiles . clear ( ) ;
2016-10-01 03:25:28 +00:00
// Delete all global proplists made static (breaks
// cyclic references).
for ( C4Value & value : OwnedPropLists )
{
C4PropList * plist = value . getPropList ( ) ;
if ( plist )
{
if ( plist - > Delete ( ) ) delete plist ;
else plist - > Clear ( ) ;
}
}
OwnedPropLists . clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulScriptEngine : : RegisterGlobalConstant ( const char * szName , const C4Value & rValue )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Register name and set value.
// AddName returns the index of existing element if the name is assigned already.
// That is OK, since it will only change the value of the global ("overload" it).
// A warning would be nice here. However, this warning would show up whenever a script
// containing globals constants is recompiled.
GlobalConsts [ GlobalConstNames . AddName ( szName ) ] = rValue ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4AulScriptEngine : : GetGlobalConstant ( const char * szName , C4Value * pTargetValue )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get index of global by name
int32_t iConstIndex = GlobalConstNames . GetItemNr ( szName ) ;
// not found?
if ( iConstIndex < 0 ) return false ;
// if it's found, assign the value if desired
if ( pTargetValue ) * pTargetValue = GlobalConsts [ iConstIndex ] ;
// constant exists
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-01-03 19:44:05 +00:00
void C4AulScriptEngine : : Denumerate ( C4ValueNumbers * numbers )
2010-03-28 18:58:01 +00:00
{
2011-03-26 22:59:35 +00:00
GlobalNamed . Denumerate ( numbers ) ;
2009-05-08 13:28:41 +00:00
// runtime data only: don't denumerate consts
2015-12-23 22:10:48 +00:00
GameScript . Denumerate ( numbers ) ;
2016-01-03 19:44:05 +00:00
C4PropListStaticMember : : Denumerate ( numbers ) ;
2015-12-19 18:53:08 +00:00
if ( pGlobalEffects ) pGlobalEffects - > Denumerate ( numbers ) ;
2010-03-28 18:58:01 +00:00
}
2009-04-21 21:44:56 +00:00
2016-05-04 01:44:59 +00:00
static void GlobalEffectsMergeCompileFunc ( StdCompiler * pComp , C4Effect * & pEffects , const char * name , C4PropList * pForObj , C4ValueNumbers * numbers )
2015-12-23 22:10:48 +00:00
{
C4Effect * pOldEffect , * pNextOldEffect = pEffects ;
2016-11-02 23:58:02 +00:00
pEffects = nullptr ;
2015-12-23 22:10:48 +00:00
try
{
2016-05-04 01:44:59 +00:00
pComp - > Value ( mkParAdapt ( mkNamingPtrAdapt ( pEffects , name ) , pForObj , numbers ) ) ;
2015-12-23 22:10:48 +00:00
}
catch ( . . . )
{
delete pNextOldEffect ;
throw ;
}
while ( ( pOldEffect = pNextOldEffect ) )
{
pNextOldEffect = pOldEffect - > pNext ;
pOldEffect - > Register ( & pEffects , Abs ( pOldEffect - > iPriority ) ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-04-21 21:44:56 +00:00
2015-12-19 18:53:08 +00:00
void C4AulScriptEngine : : CompileFunc ( StdCompiler * pComp , bool fScenarioSection , C4ValueNumbers * numbers )
2010-03-28 18:58:01 +00:00
{
2015-12-19 18:53:08 +00:00
if ( ! fScenarioSection )
{
assert ( UserFiles . empty ( ) ) ; // user files must not be kept open
C4ValueMapData GlobalNamedDefault ;
GlobalNamedDefault . SetNameList ( & GlobalNamedNames ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( GlobalNamed , numbers ) , " StaticVariables " , GlobalNamedDefault ) ) ;
pComp - > Value ( mkNamingAdapt ( mkParAdapt ( * GameScript . ScenPropList . _getPropList ( ) , numbers ) , " Scenario " ) ) ;
}
2017-03-11 14:05:41 +00:00
if ( pComp - > isDeserializer ( ) & & pGlobalEffects )
2015-12-19 18:53:08 +00:00
{
2016-09-07 01:47:29 +00:00
// loading scenario section or game re-init: Merge effects
2015-12-19 18:53:08 +00:00
// Must keep old effects here even if they're dead, because the LoadScenarioSection call typically came from execution of a global effect
// and otherwise dead pointers would remain on the stack
2016-05-04 01:44:59 +00:00
GlobalEffectsMergeCompileFunc ( pComp , pGlobalEffects , " Effects " , this , numbers ) ;
GlobalEffectsMergeCompileFunc ( pComp , GameScript . pScenarioEffects , " ScenarioEffects " , GameScript . ScenPropList . _getPropList ( ) , numbers ) ;
2015-12-19 18:53:08 +00:00
}
else
{
// Otherwise, just compile effects
2016-05-04 01:44:59 +00:00
pComp - > Value ( mkParAdapt ( mkNamingPtrAdapt ( pGlobalEffects , " Effects " ) , this , numbers ) ) ;
pComp - > Value ( mkParAdapt ( mkNamingPtrAdapt ( GameScript . pScenarioEffects , " ScenarioEffects " ) , GameScript . ScenPropList . _getPropList ( ) , numbers ) ) ;
2015-12-19 18:53:08 +00:00
}
pComp - > Value ( mkNamingAdapt ( * numbers , " Values " ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-04-21 21:44:56 +00:00
2012-05-18 15:49:38 +00:00
std : : list < const char * > C4AulScriptEngine : : GetFunctionNames ( C4PropList * p )
2011-01-13 00:09:06 +00:00
{
2011-10-13 22:40:55 +00:00
std : : list < const char * > functions ;
2012-05-18 15:49:38 +00:00
std : : list < const char * > global_functions ;
2015-09-27 00:10:55 +00:00
auto sort_alpha = [ ] ( const char * const & a , const char * const & b ) - > bool { return strcmp ( a , b ) < 0 ; } ;
2012-05-18 15:49:38 +00:00
if ( ! p ) p = GetPropList ( ) ;
const C4ValueArray * a = p - > GetProperties ( ) ;
for ( int i = 0 ; i < a - > GetSize ( ) ; + + i )
2011-01-13 00:09:06 +00:00
{
2012-05-18 15:49:38 +00:00
C4String * key = ( * a ) [ i ] . getStr ( ) ;
if ( ! key ) continue ;
C4AulFunc * f = p - > GetFunc ( key ) ;
if ( ! f ) continue ;
if ( ! f - > GetPublic ( ) ) continue ;
2015-09-27 00:10:55 +00:00
if ( ! : : ScriptEngine . GetPropList ( ) - > HasProperty ( key ) )
2012-05-18 15:49:38 +00:00
functions . push_back ( key - > GetCStr ( ) ) ;
else
global_functions . push_back ( key - > GetCStr ( ) ) ;
2011-01-13 00:09:06 +00:00
}
2012-05-18 15:49:38 +00:00
delete a ;
2015-09-27 00:10:55 +00:00
functions . sort ( sort_alpha ) ;
2017-05-03 18:28:00 +00:00
if ( ! functions . empty ( ) & & ! global_functions . empty ( ) ) functions . push_back ( nullptr ) ; // separator
2015-09-27 00:10:55 +00:00
global_functions . sort ( sort_alpha ) ;
2012-05-18 15:49:38 +00:00
functions . splice ( functions . end ( ) , global_functions ) ;
2011-01-13 00:09:06 +00:00
return functions ;
}
2013-12-22 22:47:40 +00:00
int32_t C4AulScriptEngine : : CreateUserFile ( )
{
// create new file and return handle
// find empty handle
int32_t last_handle = 1 ;
for ( std : : list < C4AulUserFile > : : const_iterator i = UserFiles . begin ( ) ; i ! = UserFiles . end ( ) ; + + i )
if ( ( * i ) . GetHandle ( ) > = last_handle )
last_handle = ( * i ) . GetHandle ( ) + 1 ;
// Create new user file
2017-05-03 18:28:00 +00:00
UserFiles . emplace_back ( last_handle ) ;
2013-12-22 22:47:40 +00:00
return last_handle ;
}
void C4AulScriptEngine : : CloseUserFile ( int32_t handle )
{
// close user file given by handle
for ( std : : list < C4AulUserFile > : : iterator i = UserFiles . begin ( ) ; i ! = UserFiles . end ( ) ; + + i )
if ( ( * i ) . GetHandle ( ) = = handle )
{
UserFiles . erase ( i ) ;
break ;
}
}
C4AulUserFile * C4AulScriptEngine : : GetUserFile ( int32_t handle )
{
// get user file given by handle
2017-05-03 18:28:00 +00:00
for ( auto & UserFile : UserFiles )
if ( UserFile . GetHandle ( ) = = handle )
2013-12-22 22:47:40 +00:00
{
2017-05-03 18:28:00 +00:00
return & UserFile ;
2013-12-22 22:47:40 +00:00
}
// not found
2016-11-02 23:58:02 +00:00
return nullptr ;
2013-12-22 22:47:40 +00:00
}
2016-07-25 12:57:54 +00:00
void C4AulScriptEngine : : RegisterErrorHandler ( C4AulErrorHandler * handler )
{
assert ( ErrorHandler = = & DefaultErrorHandler ) ;
ErrorHandler = handler ;
}
void C4AulScriptEngine : : UnregisterErrorHandler ( C4AulErrorHandler * handler )
{
assert ( ErrorHandler = = handler ) ;
ErrorHandler = & DefaultErrorHandler ;
}
2009-05-08 13:28:41 +00:00
/*--- C4AulFuncMap ---*/
2017-05-07 11:50:00 +00:00
C4AulFuncMap : : C4AulFuncMap ( )
2010-03-28 18:58:01 +00:00
{
2017-05-07 11:50:00 +00:00
memset ( Funcs , 0 , sizeof ( Funcs ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4AulFuncMap : : ~ C4AulFuncMap ( )
2010-03-28 18:58:01 +00:00
{
2012-05-27 22:53:41 +00:00
assert ( ! FuncCnt ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
unsigned int C4AulFuncMap : : Hash ( const char * name )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Fowler/Noll/Vo hash
unsigned int h = 2166136261u ;
while ( * name )
h = ( h ^ * ( name + + ) ) * 16777619 ;
return h ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-02-01 01:14:54 +00:00
C4AulFunc * C4AulFuncMap : : GetFirstFunc ( const char * Name )
2010-03-28 18:58:01 +00:00
{
2016-11-02 23:58:02 +00:00
if ( ! Name ) return nullptr ;
2016-02-01 01:14:54 +00:00
C4AulFunc * Func = Funcs [ Hash ( Name ) % HashSize ] ;
while ( Func & & ! SEqual ( Name , Func - > GetName ( ) ) )
2009-05-08 13:28:41 +00:00
Func = Func - > MapNext ;
return Func ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4AulFunc * C4AulFuncMap : : GetNextSNFunc ( const C4AulFunc * After )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4AulFunc * Func = After - > MapNext ;
2012-05-27 22:53:41 +00:00
while ( Func & & After - > GetName ( ) ! = Func - > GetName ( ) )
2009-05-08 13:28:41 +00:00
Func = Func - > MapNext ;
return Func ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2012-05-27 22:53:41 +00:00
void C4AulFuncMap : : Add ( C4AulFunc * func )
2010-03-28 18:58:01 +00:00
{
2012-05-27 22:53:41 +00:00
+ + FuncCnt ;
2009-05-08 13:28:41 +00:00
// Get a pointer to the bucket
2012-05-27 22:53:41 +00:00
C4AulFunc * * pFunc = & ( Funcs [ Hash ( func - > GetName ( ) ) % HashSize ] ) ;
// move the current first to the second position
func - > MapNext = * pFunc ;
2009-05-08 13:28:41 +00:00
// Add the func
* pFunc = func ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulFuncMap : : Remove ( C4AulFunc * func )
2010-03-28 18:58:01 +00:00
{
2012-05-27 22:53:41 +00:00
C4AulFunc * * pFunc = & Funcs [ Hash ( func - > GetName ( ) ) % HashSize ] ;
2009-05-08 13:28:41 +00:00
while ( * pFunc ! = func )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pFunc = & ( ( * pFunc ) - > MapNext ) ;
assert ( * pFunc ) ; // crash on remove of a not contained func
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
* pFunc = ( * pFunc ) - > MapNext ;
- - FuncCnt ;
2010-03-28 18:58:01 +00:00
}
2016-07-25 12:57:54 +00:00
C4AulErrorHandler : : ~ C4AulErrorHandler ( )
{
: : ScriptEngine . UnregisterErrorHandler ( this ) ;
}