Skip to content
Permalink
Browse files

Script option enhancements (#118)

* Fix SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES

* Add SCRIPTOPT_SURVIVE_ATTACHED_DISCONNECT

* wip beginning work on templatization

* Use PolModule in pol-specific modules

* wip, creating PolModule with getXYZParam

* wip 1, moving getXYZ to UOExecutor

* compilable with PolObjectImp / PolApplicObj

* add type guard

* wip, change signatures

change signatures of script_method, script_method_id, and
custom_script_method

* compilable (windows)

* linux fixes

* namespace fixes

* remove uoexhelp

* formatting

* compilable

- move can_access_offline_mobiles back to uoexec
- add guildscrobj and partyscrobj sources
- move EGuildRefObjImp and EPartyRefObjImp
- wip, implement asUOExec
- formatting uoexec.cpp,.h

* finish uoexec() helper

* some cleanup
  • Loading branch information
KevinEady committed Feb 8, 2020
1 parent 7bb27f7 commit 12d6186d10422c3ad4313ca9ac0ca3cb3da47105
Showing with 2,237 additions and 1,743 deletions.
  1. +32 −22 cmake/parse_modules.cmake
  2. +9 −1 docs/docs.polserver.com/pol100/corechanges.xml
  3. +1 −0 docs/docs.polserver.com/pol100/osem.xml
  4. +15 −15 pol-core/bscript/execmodl.h
  5. +8 −0 pol-core/bscript/executor.h
  6. +5 −0 pol-core/doc/core-changes.txt
  7. +8 −2 pol-core/pol/CMakeSources.cmake
  8. +4 −3 pol-core/pol/accounts/acscrobj.cpp
  9. +9 −9 pol-core/pol/accounts/acscrobj.h
  10. +3 −2 pol-core/pol/door.h
  11. +4 −4 pol-core/pol/exscrobj.cpp
  12. +6 −5 pol-core/pol/exscrobj.h
  13. +6 −6 pol-core/pol/getmsg.cpp
  14. +63 −0 pol-core/pol/guildscrobj.cpp
  15. +52 −0 pol-core/pol/guildscrobj.h
  16. +1 −1 pol-core/pol/item/item.cpp
  17. +3 −3 pol-core/pol/item/item.h
  18. +21 −6 pol-core/pol/mobile/charactr.cpp
  19. +3 −3 pol-core/pol/mobile/charactr.h
  20. +3 −3 pol-core/pol/mobile/npc.h
  21. +17 −16 pol-core/pol/module/attributemod.cpp
  22. +5 −4 pol-core/pol/module/attributemod.h
  23. +1 −1 pol-core/pol/module/basiciomod.cpp
  24. +4 −3 pol-core/pol/module/basiciomod.h
  25. +1 −1 pol-core/pol/module/basicmod.cpp
  26. +2 −1 pol-core/pol/module/basicmod.h
  27. +8 −9 pol-core/pol/module/boatmod.cpp
  28. +2 −2 pol-core/pol/module/boatmod.h
  29. +1 −1 pol-core/pol/module/cfgmod.cpp
  30. +4 −3 pol-core/pol/module/cfgmod.h
  31. +4 −5 pol-core/pol/module/clmod.cpp
  32. +3 −2 pol-core/pol/module/clmod.h
  33. +1 −1 pol-core/pol/module/datastore.cpp
  34. +2 −1 pol-core/pol/module/datastore.h
  35. +1 −1 pol-core/pol/module/filemod.cpp
  36. +4 −3 pol-core/pol/module/filemod.h
  37. +11 −74 pol-core/pol/module/guildmod.cpp
  38. +2 −2 pol-core/pol/module/guildmod.h
  39. +10 −9 pol-core/pol/module/httpmod.cpp
  40. +2 −3 pol-core/pol/module/httpmod.h
  41. +1 −1 pol-core/pol/module/mathmod.cpp
  42. +4 −3 pol-core/pol/module/mathmod.h
  43. +12 −11 pol-core/pol/module/npcmod.cpp
  44. +4 −4 pol-core/pol/module/npcmod.h
  45. +48 −42 pol-core/pol/module/osmod.cpp
  46. +5 −11 pol-core/pol/module/osmod.h
  47. +13 −308 pol-core/pol/module/partymod.cpp
  48. +2 −3 pol-core/pol/module/partymod.h
  49. +5 −5 pol-core/pol/module/polsystemmod.cpp
  50. +11 −6 pol-core/pol/module/polsystemmod.h
  51. +4 −5 pol-core/pol/module/sqlmod.cpp
  52. +2 −5 pol-core/pol/module/sqlmod.h
  53. +2 −3 pol-core/pol/module/storagemod.cpp
  54. +3 −2 pol-core/pol/module/storagemod.h
  55. +4 −4 pol-core/pol/module/unimod.cpp
  56. +5 −27 pol-core/pol/module/unimod.h
  57. +131 −134 pol-core/pol/module/uomod.cpp
  58. +2 −4 pol-core/pol/module/uomod.h
  59. +52 −49 pol-core/pol/module/uomod2.cpp
  60. +1 −2 pol-core/pol/module/uomod3.cpp
  61. +1 −2 pol-core/pol/module/uomod4.cpp
  62. +1 −1 pol-core/pol/module/utilmod.cpp
  63. +4 −3 pol-core/pol/module/utilmod.h
  64. +12 −13 pol-core/pol/module/vitalmod.cpp
  65. +2 −2 pol-core/pol/module/vitalmod.h
  66. +3 −2 pol-core/pol/multi/boat.h
  67. +4 −5 pol-core/pol/multi/house.cpp
  68. +3 −2 pol-core/pol/multi/house.h
  69. +5 −4 pol-core/pol/network/auxclient.cpp
  70. +5 −4 pol-core/pol/network/auxclient.h
  71. +9 −9 pol-core/pol/network/cgdata.cpp
  72. +9 −9 pol-core/pol/packetscrobj.cpp
  73. +8 −8 pol-core/pol/packetscrobj.h
  74. +323 −0 pol-core/pol/partyscrobj.cpp
  75. +52 −0 pol-core/pol/partyscrobj.h
  76. +4 −3 pol-core/pol/poldbg.cpp
  77. +105 −0 pol-core/pol/polmodl.cpp
  78. +63 −0 pol-core/pol/polmodl.h
  79. +35 −0 pol-core/pol/polobject.cpp
  80. +87 −0 pol-core/pol/polobject.h
  81. +3 −2 pol-core/pol/spelbook.h
  82. +8 −8 pol-core/pol/sqlscrobj.cpp
  83. +6 −5 pol-core/pol/sqlscrobj.h
  84. +2 −0 pol-core/pol/syshook.cpp
  85. +3 −2 pol-core/pol/umap.cpp
  86. +3 −2 pol-core/pol/umap.h
  87. +4 −3 pol-core/pol/uobject.h
  88. +671 −6 pol-core/pol/uoexec.cpp
  89. +52 −1 pol-core/pol/uoexec.h
  90. +0 −644 pol-core/pol/uoexhelp.cpp
  91. +0 −56 pol-core/pol/uoexhelp.h
  92. +56 −52 pol-core/pol/uoscrobj.cpp
  93. +40 −39 pol-core/pol/uoscrobj.h
  94. +2 −0 pol-core/support/scripts/os.em
@@ -2,27 +2,31 @@
# Maps a .em filename to a tuple of:
# - C++ class name
# - module name
set(MODMAP_attributes AttributeExecutorModule attributemod)
set(MODMAP_basic BasicExecutorModule basicmod)
set(MODMAP_basicio BasicIoExecutorModule basiciomod)
set(MODMAP_boat UBoatExecutorModule boatmod)
set(MODMAP_cfgfile ConfigFileExecutorModule cfgmod)
set(MODMAP_cliloc ClilocExecutorModule clmod)
set(MODMAP_datafile DataFileExecutorModule datastore)
set(MODMAP_file FileAccessExecutorModule filemod)
set(MODMAP_guilds GuildExecutorModule guildmod)
set(MODMAP_http HttpExecutorModule httpmod)
set(MODMAP_math MathExecutorModule mathmod)
set(MODMAP_npc NPCExecutorModule npcmod)
set(MODMAP_os OSExecutorModule osmod)
set(MODMAP_party PartyExecutorModule partymod)
set(MODMAP_polsys PolSystemExecutorModule polsystemmod)
set(MODMAP_sql SQLExecutorModule sqlmod)
set(MODMAP_storage StorageExecutorModule storagemod)
set(MODMAP_unicode UnicodeExecutorModule unimod)
set(MODMAP_uo UOExecutorModule uomod)
set(MODMAP_util UtilExecutorModule utilmod)
set(MODMAP_vitals VitalExecutorModule vitalmod)

# These modules are normal ExecutorModules, available to runecl
set(MODMAP_basic BasicExecutorModule basicmod 1)
set(MODMAP_basicio BasicIoExecutorModule basiciomod 1)
set(MODMAP_cfgfile ConfigFileExecutorModule cfgmod 1)
set(MODMAP_datafile DataFileExecutorModule datastore 1)
set(MODMAP_file FileAccessExecutorModule filemod 1)
set(MODMAP_math MathExecutorModule mathmod 1)
set(MODMAP_util UtilExecutorModule utilmod 1)

# The remaining modules are PolModules, not usable by runecl and only within pol
set(MODMAP_attributes AttributeExecutorModule attributemod 0)
set(MODMAP_boat UBoatExecutorModule boatmod 0)
set(MODMAP_cliloc ClilocExecutorModule clmod 0)
set(MODMAP_guilds GuildExecutorModule guildmod 0)
set(MODMAP_http HttpExecutorModule httpmod 0)
set(MODMAP_npc NPCExecutorModule npcmod 0)
set(MODMAP_os OSExecutorModule osmod 0)
set(MODMAP_party PartyExecutorModule partymod 0)
set(MODMAP_polsys PolSystemExecutorModule polsystemmod 0)
set(MODMAP_sql SQLExecutorModule sqlmod 0)
set(MODMAP_storage StorageExecutorModule storagemod 0)
set(MODMAP_unicode UnicodeExecutorModule unimod 0)
set(MODMAP_uo UOExecutorModule uomod 0)
set(MODMAP_vitals VitalExecutorModule vitalmod 0)

if(NOT EM_FOLDER)
message(FATAL_ERROR "No EM_FOLDER set")
@@ -106,12 +110,18 @@ function(createfunctable)
# Get module settings from map
LIST(GET MODMAP_${em_name} 0 MODCLASS)
LIST(GET MODMAP_${em_name} 1 MODHEADER)
LIST(GET MODMAP_${em_name} 2 IS_RUNECL_AVAIL)
if(IS_RUNECL_AVAIL)
set(MODPARENT "Bscript::ExecutorModule")
else()
set(MODPARENT "Core::PolModule")
endif()

set(HEADER "#ifndef ${MODCLASS}_EM_h\n#define ${MODCLASS}_EM_h\n")
set(HEADER "${HEADER}namespace Pol\n{\nnamespace Bscript\n{\nusing namespace Module\;\n\n")

# message("Creating function table definition for ${em} => ${em_name} => ${MODCLASS}")
set(MODDEF "template <>\nconst char* const TmplExecutorModule<${MODCLASS}>::modname = \"${em_name}\"\;\n\ntemplate <>\nTmplExecutorModule<${MODCLASS}>::FunctionTable\n TmplExecutorModule<${MODCLASS}>::function_table = {\n")
set(MODDEF "template <>\nconst char* const TmplExecutorModule<${MODCLASS}, ${MODPARENT}>::modname = \"${em_name}\"\;\n\ntemplate <>\nTmplExecutorModule<${MODCLASS}, ${MODPARENT}>::FunctionTable\n TmplExecutorModule<${MODCLASS}, ${MODPARENT}>::function_table = {\n")
FILE(READ ${em} contents)
STRING(REGEX REPLACE ";" "\\\\;" contents "${contents}")
read_def(${MODCLASS} ${contents} FUNCTBL)
@@ -2,9 +2,17 @@
<ESCRIPT>
<header>
<topic>Latest Core Changes</topic>
<datemodified>01-20-2020</datemodified>
<datemodified>02-04-2020</datemodified>
</header>
<version name="POL100">
<entry>
<date>02-05-2020</date>
<author>Kevin:</author>
<change type="Added">New SCRIPTOPT_SURVIVE_ATTACHED_DISCONNECT will keep attached scripts (eg. skill scripts, item use scripts) alive if the<br/>
character's client disconnects. Note that by default all references to the character will become invalidated; for example,<br/>
GetObjProperty(chr, &quot;prop&quot;) will return an error. Enable SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES to bypass this.</change>
<change type="Fixed">Scripts that have Set_Script_Option(SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES) can correctly access offline mobiles.</change>
</entry>
<entry>
<date>01-20-2020</date>
<author>Kevin:</author>
@@ -11,6 +11,7 @@
<constant>const SCRIPTOPT_NO_RUNAWAY := 3; // if 1, doesn't warn about runaway conditions</constant>
<constant>const SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES := 4;</constant>
<constant>const SCRIPTOPT_AUXSVC_ASSUME_STRING := 5;</constant>
<constant>const SCRIPTOPT_SURVIVE_ATTACHED_DISCONNECT := 6; // if 1, do not kill script if attached character's client disconnects</constant>
</fileheader>

<function name="GetProcess">
@@ -98,8 +98,8 @@ BApplicObj<T>* getApplicObjParam( ExecutorModule& ex, unsigned param,
return static_cast<BApplicObj<T>*>( ex.getApplicObjParam( param, object_type ) );
}

template <class T>
class TmplExecutorModule : public ExecutorModule
template <class T, class T2>
class TmplExecutorModule : public T2
{
public:
static const char* const modname;
@@ -126,15 +126,15 @@ class TmplExecutorModule : public ExecutorModule
virtual std::string functionName( unsigned idx ) override;
};

template <class T>
std::map<std::string, int, Clib::ci_cmp_pred> TmplExecutorModule<T>::_func_idx_map;
template <class T, class T2>
std::map<std::string, int, Clib::ci_cmp_pred> TmplExecutorModule<T, T2>::_func_idx_map;

template <class T>
bool TmplExecutorModule<T>::_func_map_init = false;
template <class T, class T2>
bool TmplExecutorModule<T, T2>::_func_map_init = false;

template <class T>
TmplExecutorModule<T>::TmplExecutorModule( Executor& ex )
: ExecutorModule( TmplExecutorModule::modname, ex )
template <class T, class T2>
TmplExecutorModule<T, T2>::TmplExecutorModule( Executor& ex )
: T2( TmplExecutorModule::modname, ex )
{
if ( !_func_map_init )
{
@@ -146,24 +146,24 @@ TmplExecutorModule<T>::TmplExecutorModule( Executor& ex )
}
}

template <class T>
inline int TmplExecutorModule<T>::functionIndex( const std::string& name )
template <class T, class T2>
inline int TmplExecutorModule<T, T2>::functionIndex( const std::string& name )
{
auto itr = _func_idx_map.find( name );
if ( itr != _func_idx_map.end() )
return itr->second;
return -1;
}

template <class T>
inline BObjectImp* TmplExecutorModule<T>::execFunc( unsigned funcidx )
template <class T, class T2>
inline BObjectImp* TmplExecutorModule<T, T2>::execFunc( unsigned funcidx )
{
T* derived = static_cast<T*>( this );
return ( ( *derived ).*( function_table[funcidx].fptr ) )();
};

template <class T>
inline std::string TmplExecutorModule<T>::functionName( unsigned idx )
template <class T, class T2>
inline std::string TmplExecutorModule<T, T2>::functionName( unsigned idx )
{
return function_table[idx].funcname;
}
@@ -78,6 +78,12 @@ struct BackupStruct
unsigned PC;
};

enum class ExecutorType
{
EXECUTOR,
POL
};

class Executor
{
public:
@@ -92,6 +98,8 @@ class Executor
bool halt_;
bool run_ok_;

virtual ExecutorType type() { return ExecutorType::EXECUTOR; }

enum DEBUG_LEVEL
{
NONE,
@@ -1,4 +1,9 @@
-- POL100 --
02-05-2020 Kevin:
Added: New SCRIPTOPT_SURVIVE_ATTACHED_DISCONNECT will keep attached scripts (eg. skill scripts, item use scripts) alive if the
character's client disconnects. Note that by default all references to the character will become invalidated; for example,
GetObjProperty(chr, "prop") will return an error. Enable SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES to bypass this.
Fixed: Scripts that have Set_Script_Option(SCRIPTOPT_CAN_ACCESS_OFFLINE_MOBILES) can correctly access offline mobiles.
01-20-2020 Kevin:
Fixed: Huge Performance loss in Lower/Upper String functions.
01-18-2020 DevGIB:
@@ -100,6 +100,8 @@ set (pol_sources # sorted !
guardrgn.h
guilds.cpp
guilds.h
guildscrobj.cpp
guildscrobj.h
help.cpp
irequest.cpp
item/armor.cpp
@@ -281,6 +283,8 @@ set (pol_sources # sorted !
party.cpp
party.h
party_cfg.h
partyscrobj.cpp
partyscrobj.h
pol.cpp
pol.h
pol.rc
@@ -291,6 +295,10 @@ set (pol_sources # sorted !
polclock.h
poldbg.cpp
poldbg.h
polmodl.cpp
polmodl.h
polobject.cpp
polobject.h
polresource.h
polsem.cpp
polsem.h
@@ -402,8 +410,6 @@ set (pol_sources # sorted !
uoclient.h
uoexec.cpp
uoexec.h
uoexhelp.cpp
uoexhelp.h
uolisten.cpp
uopathnode.h
uoscrobj.cpp
@@ -38,6 +38,7 @@
#include "../network/client.h"
#include "../polcfg.h"
#include "../ufunc.h"
#include "../uoexec.h"
#include "../uoscrobj.h"
#include "account.h"
#include "accounts.h"
@@ -77,7 +78,7 @@ Bscript::BObjectImp* AccountObjImp::copy() const
/// All methods return Error("Not enough parameters") if too few parameters were passed.
/// All methods return Error("Invalid parameter type") if the wrong type was passed.
///
Bscript::BObjectImp* AccountObjImp::call_method_id( const int id, Bscript::Executor& ex,
Bscript::BObjectImp* AccountObjImp::call_polmethod_id( const int id, Core::UOExecutor& ex,
bool forcebuiltin )
{
using namespace Bscript;
@@ -552,12 +553,12 @@ Bscript::BObjectImp* AccountObjImp::call_method_id( const int id, Bscript::Execu
/// All methods return Error("Not enough parameters") if too few parameters were passed.
/// All methods return Error("Invalid parameter type") if the wrong type was passed.
///
Bscript::BObjectImp* AccountObjImp::call_method( const char* methodname, Bscript::Executor& ex )
Bscript::BObjectImp* AccountObjImp::call_polmethod( const char* methodname, Core::UOExecutor& ex )
{
bool forcebuiltin{Bscript::Executor::builtinMethodForced( methodname )};
Bscript::ObjMethod* objmethod = Bscript::getKnownObjMethod( methodname );
if ( objmethod != nullptr )
return this->call_method_id( objmethod->id, ex, forcebuiltin );
return this->call_polmethod_id( objmethod->id, ex, forcebuiltin );
return Core::gamestate.system_hooks.call_script_method( methodname, &ex, this );
}

@@ -8,8 +8,8 @@
#ifndef ACSCROBJ_H
#define ACSCROBJ_H

#include "../../bscript/bobject.h"
#include "../../clib/rawtypes.h"
#include "../polobject.h"
#include "../reftypes.h"

namespace Pol
@@ -41,25 +41,25 @@ class AccountPtrHolder
};

extern Bscript::BApplicObjType accountobjimp_type;
class AccountObjImp final : public Bscript::BApplicObj<AccountPtrHolder>
class AccountObjImp final : public Core::PolApplicObj<AccountPtrHolder>
{
typedef Bscript::BApplicObj<AccountPtrHolder> base;
typedef Core::PolApplicObj<AccountPtrHolder> base;

public:
explicit AccountObjImp( const AccountPtrHolder& other )
: Bscript::BApplicObj<AccountPtrHolder>( &accountobjimp_type, other )
: PolApplicObj<AccountPtrHolder>( &accountobjimp_type, other )
{
}
virtual const char* typeOf() const override;
virtual u8 typeOfInt() const override;
virtual Bscript::BObjectImp* copy() const override;
virtual Bscript::BObjectImp* call_method( const char* methodname,
Bscript::Executor& ex ) override;
virtual Bscript::BObjectImp* call_method_id( const int id, Bscript::Executor& ex,
virtual Bscript::BObjectImp* call_polmethod( const char* methodname,
Core::UOExecutor& ex ) override;
virtual Bscript::BObjectImp* call_polmethod_id( const int id, Core::UOExecutor& ex,
bool forcebuiltin = false ) override;
virtual Bscript::BObjectRef get_member( const char* membername ) override;
virtual Bscript::BObjectRef get_member_id( const int id ) override; // id test
};
}
}
} // namespace Accounts
} // namespace Pol
#endif
@@ -36,6 +36,7 @@ class DoorDesc;
namespace Core
{
class ExportScript;
class UOExecutor;
class UDoor final : public ULockable
{
typedef ULockable base;
@@ -55,8 +56,8 @@ class UDoor final : public ULockable
virtual Bscript::BObjectImp* get_script_member( const char* membername ) const override;
virtual Bscript::BObjectImp* get_script_member_id( const int id ) const override; /// id test
virtual Bscript::BObjectImp* script_method( const char* methodname,
Bscript::Executor& ex ) override;
virtual Bscript::BObjectImp* script_method_id( const int id, Bscript::Executor& ex ) override;
UOExecutor& ex ) override;
virtual Bscript::BObjectImp* script_method_id( const int id, UOExecutor& ex ) override;
virtual bool script_isa( unsigned isatype ) const override;

protected:
@@ -41,7 +41,7 @@ using namespace Bscript;
BApplicObjType scriptexobjimp_type;

ScriptExObjImp::ScriptExObjImp( UOExecutor* uoexec )
: BApplicObj<ScriptExPtr>( &scriptexobjimp_type, uoexec->weakptr )
: PolApplicObj<ScriptExPtr>( &scriptexobjimp_type, uoexec->weakptr )
{
}

@@ -63,7 +63,7 @@ BObjectImp* ScriptExObjImp::copy() const
}


BObjectImp* ScriptExObjImp::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ )
BObjectImp* ScriptExObjImp::call_polmethod_id( const int id, UOExecutor& ex, bool /*forcebuiltin*/ )
{
if ( !value().exists() )
return new BError( "Script has been destroyed" );
@@ -135,11 +135,11 @@ BObjectImp* ScriptExObjImp::call_method_id( const int id, Executor& ex, bool /*f
}
}

BObjectImp* ScriptExObjImp::call_method( const char* methodname, Executor& ex )
BObjectImp* ScriptExObjImp::call_polmethod( const char* methodname, UOExecutor& ex )
{
ObjMethod* objmethod = getKnownObjMethod( methodname );
if ( objmethod != nullptr )
return this->call_method_id( objmethod->id, ex );
return this->call_polmethod_id( objmethod->id, ex );
else
return new BError( "undefined" );
}
@@ -15,6 +15,7 @@

#include "../clib/rawtypes.h"
#include "../clib/weakptr.h"
#include "polobject.h"

namespace Pol
{
@@ -32,18 +33,18 @@ class UOExecutor;

extern Bscript::BApplicObjType scriptexobjimp_type;
typedef weak_ptr<UOExecutor> ScriptExPtr;
class ScriptExObjImp final : public Bscript::BApplicObj<ScriptExPtr>
class ScriptExObjImp final : public PolApplicObj<ScriptExPtr>
{
typedef Bscript::BApplicObj<ScriptExPtr> base;
typedef PolApplicObj<ScriptExPtr> base;

public:
explicit ScriptExObjImp( UOExecutor* uoexec );
virtual const char* typeOf() const override;
virtual u8 typeOfInt() const override;
virtual Bscript::BObjectImp* copy() const override;
virtual Bscript::BObjectImp* call_method( const char* methodname,
Bscript::Executor& ex ) override;
virtual Bscript::BObjectImp* call_method_id( const int id, Bscript::Executor& ex,
virtual Bscript::BObjectImp* call_polmethod( const char* methodname,
Core::UOExecutor& ex ) override;
virtual Bscript::BObjectImp* call_polmethod_id( const int id, Core::UOExecutor& ex,
bool forcebuiltin = false ) override;
virtual Bscript::BObjectRef get_member( const char* membername ) override;
virtual Bscript::BObjectRef get_member_id( const int id ) override;

0 comments on commit 12d6186

Please sign in to comment.
You can’t perform that action at this time.