Actual implementation of Angelscript hooks into battle library.
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2020-02-04 19:34:30 +01:00
parent 0455fec875
commit bcc038b49d
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
11 changed files with 717 additions and 72 deletions

View File

@ -40,7 +40,7 @@ class PkmnLibConan(ConanFile):
self.options["AngelScript"].link_std_statically = True self.options["AngelScript"].link_std_statically = True
def requirements(self): def requirements(self):
self.requires("CreatureLib/c3b573c7daa3f2815e063c9dabfaa5dbfe6dbb20@creaturelib/master") self.requires("CreatureLib/cd7ddcf78ec0ee4ae645d1862f92ec23246af949@creaturelib/master")
if self.options.script_handler == "angelscript": if self.options.script_handler == "angelscript":
self.requires("AngelScript/2.34@AngelScript/Deukhoofd") self.requires("AngelScript/2.34@AngelScript/Deukhoofd")
else: else:

View File

@ -0,0 +1,360 @@
#include "scripthandle.h"
#include <new>
#include <assert.h>
#include <string.h>
BEGIN_AS_NAMESPACE
static void Construct(CScriptHandle *self) { new(self) CScriptHandle(); }
static void Construct(CScriptHandle *self, const CScriptHandle &o) { new(self) CScriptHandle(o); }
// This one is not static because it needs to be friend with the CScriptHandle class
void Construct(CScriptHandle *self, void *ref, int typeId) { new(self) CScriptHandle(ref, typeId); }
static void Destruct(CScriptHandle *self) { self->~CScriptHandle(); }
CScriptHandle::CScriptHandle()
{
m_ref = 0;
m_type = 0;
}
CScriptHandle::CScriptHandle(const CScriptHandle &other)
{
m_ref = other.m_ref;
m_type = other.m_type;
AddRefHandle();
}
CScriptHandle::CScriptHandle(void *ref, asITypeInfo *type)
{
m_ref = ref;
m_type = type;
AddRefHandle();
}
// This constructor shouldn't be called from the application
// directly as it requires an active script context
CScriptHandle::CScriptHandle(void *ref, int typeId)
{
m_ref = 0;
m_type = 0;
Assign(ref, typeId);
}
CScriptHandle::~CScriptHandle()
{
ReleaseHandle();
}
void CScriptHandle::ReleaseHandle()
{
if( m_ref && m_type )
{
asIScriptEngine *engine = m_type->GetEngine();
engine->ReleaseScriptObject(m_ref, m_type);
engine->Release();
m_ref = 0;
m_type = 0;
}
}
void CScriptHandle::AddRefHandle()
{
if( m_ref && m_type )
{
asIScriptEngine *engine = m_type->GetEngine();
engine->AddRefScriptObject(m_ref, m_type);
// Hold on to the engine so it isn't destroyed while
// a reference to a script object is still held
engine->AddRef();
}
}
CScriptHandle &CScriptHandle::operator =(const CScriptHandle &other)
{
Set(other.m_ref, other.m_type);
return *this;
}
void CScriptHandle::Set(void *ref, asITypeInfo *type)
{
if( m_ref == ref ) return;
ReleaseHandle();
m_ref = ref;
m_type = type;
AddRefHandle();
}
void *CScriptHandle::GetRef()
{
return m_ref;
}
asITypeInfo *CScriptHandle::GetType() const
{
return m_type;
}
int CScriptHandle::GetTypeId() const
{
if( m_type == 0 ) return 0;
return m_type->GetTypeId() | asTYPEID_OBJHANDLE;
}
// This method shouldn't be called from the application
// directly as it requires an active script context
CScriptHandle &CScriptHandle::Assign(void *ref, int typeId)
{
// When receiving a null handle we just clear our memory
if( typeId == 0 )
{
Set(0, 0);
return *this;
}
// Dereference received handles to get the object
if( typeId & asTYPEID_OBJHANDLE )
{
// Store the actual reference
ref = *(void**)ref;
typeId &= ~asTYPEID_OBJHANDLE;
}
// Get the object type
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
asITypeInfo *type = engine->GetTypeInfoById(typeId);
// If the argument is another CScriptHandle, we should copy the content instead
if( type && strcmp(type->GetName(), "ref") == 0 )
{
CScriptHandle *r = (CScriptHandle*)ref;
ref = r->m_ref;
type = r->m_type;
}
Set(ref, type);
return *this;
}
bool CScriptHandle::operator==(const CScriptHandle &o) const
{
if( m_ref == o.m_ref &&
m_type == o.m_type )
return true;
// TODO: If type is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
return false;
}
bool CScriptHandle::operator!=(const CScriptHandle &o) const
{
return !(*this == o);
}
bool CScriptHandle::Equals(void *ref, int typeId) const
{
// Null handles are received as reference to a null handle
if( typeId == 0 )
ref = 0;
// Dereference handles to get the object
if( typeId & asTYPEID_OBJHANDLE )
{
// Compare the actual reference
ref = *(void**)ref;
typeId &= ~asTYPEID_OBJHANDLE;
}
// TODO: If typeId is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
if( ref == m_ref ) return true;
return false;
}
// AngelScript: used as '@obj = cast<obj>(ref);'
void CScriptHandle::Cast(void **outRef, int typeId)
{
// If we hold a null handle, then just return null
if( m_type == 0 )
{
*outRef = 0;
return;
}
// It is expected that the outRef is always a handle
assert( typeId & asTYPEID_OBJHANDLE );
// Compare the type id of the actual object
typeId &= ~asTYPEID_OBJHANDLE;
asIScriptEngine *engine = m_type->GetEngine();
asITypeInfo *type = engine->GetTypeInfoById(typeId);
*outRef = 0;
// RefCastObject will increment the refCount of the returned object if successful
engine->RefCastObject(m_ref, m_type, type, outRef);
}
void CScriptHandle::EnumReferences(asIScriptEngine *inEngine)
{
// If we're holding a reference, we'll notify the garbage collector of it
if (m_ref)
inEngine->GCEnumCallback(m_ref);
// The object type itself is also garbage collected
if( m_type)
inEngine->GCEnumCallback(m_type);
}
void CScriptHandle::ReleaseReferences(asIScriptEngine *inEngine)
{
// Simply clear the content to release the references
Set(0, 0);
}
void RegisterScriptHandle_Native(asIScriptEngine *engine)
{
int r;
#if AS_CAN_USE_CPP11
// With C++11 it is possible to use asGetTypeTraits to automatically determine the flags that represent the C++ class
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptHandle>()); assert( r >= 0 );
#else
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
#endif
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTIONPR(Construct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTIONPR(Construct, (CScriptHandle *, const CScriptHandle &), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTIONPR(Construct, (CScriptHandle *, void *, int), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTIONPR(Destruct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptHandle,EnumReferences), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptHandle, ReleaseReferences), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asMETHODPR(CScriptHandle, Cast, (void **, int), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asMETHOD(CScriptHandle, operator=), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asMETHOD(CScriptHandle, Assign), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asMETHODPR(CScriptHandle, operator==, (const CScriptHandle &) const, bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asMETHODPR(CScriptHandle, Equals, (void*, int) const, bool), asCALL_THISCALL); assert( r >= 0 );
}
void CScriptHandle_Construct_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
new(self) CScriptHandle();
}
void CScriptHandle_ConstructCopy_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
new(self) CScriptHandle(*other);
}
void CScriptHandle_ConstructVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
Construct(self, ref, typeId);
}
void CScriptHandle_Destruct_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->~CScriptHandle();
}
void CScriptHandle_Cast_Generic(asIScriptGeneric *gen)
{
void **ref = reinterpret_cast<void**>(gen->GetArgAddress(0));
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->Cast(ref, typeId);
}
void CScriptHandle_Assign_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
*self = *other;
gen->SetReturnAddress(self);
}
void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->Assign(ref, typeId);
gen->SetReturnAddress(self);
}
void CScriptHandle_Equals_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
gen->SetReturnByte(*self == *other);
}
void CScriptHandle_EqualsVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
gen->SetReturnByte(self->Equals(ref, typeId));
}
void CScriptHandle_EnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->EnumReferences(gen->GetEngine());
}
void CScriptHandle_ReleaseReferences_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->ReleaseReferences(gen->GetEngine());
}
void RegisterScriptHandle_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptHandle_Construct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTION(CScriptHandle_ConstructCopy_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTION(CScriptHandle_ConstructVar_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptHandle_Destruct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(CScriptHandle_EnumReferences_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(CScriptHandle_ReleaseReferences_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asFUNCTION(CScriptHandle_Cast_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asFUNCTION(CScriptHandle_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asFUNCTION(CScriptHandle_AssignVar_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asFUNCTION(CScriptHandle_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asFUNCTION(CScriptHandle_EqualsVar_Generic), asCALL_GENERIC); assert( r >= 0 );
}
void RegisterScriptHandle(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptHandle_Generic(engine);
else
RegisterScriptHandle_Native(engine);
}
END_AS_NAMESPACE

View File

@ -0,0 +1,69 @@
#ifndef SCRIPTHANDLE_H
#define SCRIPTHANDLE_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
class CScriptHandle
{
public:
// Constructors
CScriptHandle();
CScriptHandle(const CScriptHandle &other);
CScriptHandle(void *ref, asITypeInfo *type);
~CScriptHandle();
// Copy the stored value from another any object
CScriptHandle &operator=(const CScriptHandle &other);
// Set the reference
void Set(void *ref, asITypeInfo *type);
// Compare equalness
bool operator==(const CScriptHandle &o) const;
bool operator!=(const CScriptHandle &o) const;
bool Equals(void *ref, int typeId) const;
// Dynamic cast to desired handle type
void Cast(void **outRef, int typeId);
// Returns the type of the reference held
asITypeInfo *GetType() const;
int GetTypeId() const;
// Get the reference
void *GetRef();
// GC callback
void EnumReferences(asIScriptEngine *engine);
void ReleaseReferences(asIScriptEngine *engine);
protected:
// These functions need to have access to protected
// members in order to call them from the script engine
friend void Construct(CScriptHandle *self, void *ref, int typeId);
friend void RegisterScriptHandle_Native(asIScriptEngine *engine);
friend void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen);
void ReleaseHandle();
void AddRefHandle();
// These shouldn't be called directly by the
// application as they requires an active context
CScriptHandle(void *ref, int typeId);
CScriptHandle &Assign(void *ref, int typeId);
void *m_ref;
asITypeInfo *m_type;
};
void RegisterScriptHandle(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@ -2,8 +2,11 @@
#define AS_USE_ACCESSORS #define AS_USE_ACCESSORS
#include "../../../extern/angelscript_addons/scriptarray/scriptarray.h" #include "../../../extern/angelscript_addons/scriptarray/scriptarray.h"
#undef AS_USE_ACCESSORS #undef AS_USE_ACCESSORS
#include <cassert>
#include "../../../extern/angelscript_addons/scripthandle/scripthandle.h"
#include "../../../extern/angelscript_addons/scripthelper/scripthelper.h" #include "../../../extern/angelscript_addons/scripthelper/scripthelper.h"
#include "../../../extern/angelscript_addons/scriptstdstring/scriptstdstring.h" #include "../../../extern/angelscript_addons/scriptstdstring/scriptstdstring.h"
#include "TypeRegistry/BasicScriptClass.hpp"
#include "TypeRegistry/Battling/RegisterExecutingAttack.hpp" #include "TypeRegistry/Battling/RegisterExecutingAttack.hpp"
#include "TypeRegistry/Battling/RegisterPokemonClass.hpp" #include "TypeRegistry/Battling/RegisterPokemonClass.hpp"
#include "TypeRegistry/Library/RegisterGrowthRateTypes.hpp" #include "TypeRegistry/Library/RegisterGrowthRateTypes.hpp"
@ -17,7 +20,8 @@ CreatureLib::Battling::ScriptResolver* PkmnLib::Battling::BattleLibrary::CreateS
return new AngelScripResolver(); return new AngelScripResolver();
} }
void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* library) { void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) {
auto library = (PkmnLib::Battling::BattleLibrary*)arg;
_engine = asCreateScriptEngine(); _engine = asCreateScriptEngine();
int32_t r = _engine->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL); int32_t r = _engine->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL);
@ -25,10 +29,13 @@ void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* librar
throw CreatureException("Registering message callback failed."); throw CreatureException("Registering message callback failed.");
_engine->SetEngineProperty(asEP_DISALLOW_EMPTY_LIST_ELEMENTS, true); _engine->SetEngineProperty(asEP_DISALLOW_EMPTY_LIST_ELEMENTS, true);
_engine->SetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE, true); _engine->SetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE, false);
_engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
_engine->SetEngineProperty(asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT, true); _engine->SetEngineProperty(asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT, true);
_engine->SetEngineProperty(asEP_AUTO_GARBAGE_COLLECT, false); _engine->SetEngineProperty(asEP_AUTO_GARBAGE_COLLECT, false);
_engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, true); _engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, true);
_engine->SetEngineProperty(asEP_PROPERTY_ACCESSOR_MODE, 2);
RegisterStdString(_engine); RegisterStdString(_engine);
@ -38,11 +45,15 @@ void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* librar
r = _engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(Print), asCALL_CDECL); r = _engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(Print), asCALL_CDECL);
if (r < 0) if (r < 0)
throw CreatureException("Registering print function failed."); throw CreatureException("Registering print function failed.");
RegisterScriptHandle(_engine);
_mainModule = _engine->GetModule("pkmn", asGM_ALWAYS_CREATE);
RegisterTypes(); RegisterTypes();
RegisterExceptionRoutines(_engine); RegisterExceptionRoutines(_engine);
_mainModule = _engine->GetModule("pkmn", asGM_ALWAYS_CREATE); auto staticLib = library->GetStaticLib();
_engine->RegisterGlobalProperty("const StaticLibrary@ StaticLib", &staticLib);
_contextPool = new ContextPool(_engine); _contextPool = new ContextPool(_engine);
} }
@ -59,6 +70,9 @@ void AngelScripResolver::RegisterTypes() {
// Register battle types // Register battle types
RegisterPokemonClass::Register(_engine); RegisterPokemonClass::Register(_engine);
RegisterExecutingAttack::Register(_engine); RegisterExecutingAttack::Register(_engine);
// Register base script
BasicScriptClass::Register(_engine);
} }
AngelScriptTypeInfo* AngelScripResolver::GetTypeInfo(const std::string& name) { AngelScriptTypeInfo* AngelScripResolver::GetTypeInfo(const std::string& name) {

View File

@ -3,6 +3,7 @@
#include <Battling/ScriptHandling/Script.hpp> #include <Battling/ScriptHandling/Script.hpp>
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT #define ANGELSCRIPT_DLL_LIBRARY_IMPORT
#include <angelscript.h> #include <angelscript.h>
#include <Core/Exceptions/NotImplementedException.hpp>
#include "AngelScriptTypeInfo.hpp" #include "AngelScriptTypeInfo.hpp"
#include "ContextPool.hpp" #include "ContextPool.hpp"
@ -19,25 +20,6 @@ public:
~AngelScriptScript() override { _obj->Release(); } ~AngelScriptScript() override { _obj->Release(); }
void InvokeMethod(const char* name) {
auto func = _type->GetFunction(name);
if (func == nullptr)
return;
auto ctx = _ctxPool->RequestContext();
ctx->Prepare(func);
ctx->SetObject(_obj);
auto result = ctx->Execute();
if (result != asEXECUTION_FINISHED) {
if (result == asEXECUTION_EXCEPTION) {
throw CreatureException(ctx->GetExceptionString());
}
throw CreatureException("Function failed.");
}
_ctxPool->ReturnContextToPool(ctx);
}
asIScriptObject* GetScriptObject() { return _obj; }
asIScriptFunction* PrepareMethod(const char* name, asIScriptContext* ctx) { asIScriptFunction* PrepareMethod(const char* name, asIScriptContext* ctx) {
auto func = _type->GetFunction(name); auto func = _type->GetFunction(name);
ctx->Prepare(func); ctx->Prepare(func);
@ -46,6 +28,89 @@ public:
} }
ContextPool* GetContextPool() { return _ctxPool; } ContextPool* GetContextPool() { return _ctxPool; }
#define CALLHOOK(name, setup) \
auto s = _type->Get##name(); \
if (!s.Exists) \
return; \
auto ctx = _ctxPool->RequestContext(); \
ctx->Prepare(s.Function); \
ctx->SetObject(_obj); \
setup; \
auto scriptResult = ctx->Execute(); \
if (scriptResult != 0) { \
throw CreatureException("Script didn't finish properly; message " + std::to_string(scriptResult)); \
} \
_ctxPool->ReturnContextToPool(ctx);
void Stack() override { CALLHOOK(Stack, {}); }
void OnBeforeTurn(const CreatureLib::Battling::BaseTurnChoice* choice) override {
throw NotImplementedException(); //TODO
}
void ChangeAttack(CreatureLib::Battling::AttackTurnChoice* choice, std::string* outAttack) override {
throw NotImplementedException(); //TODO
}
void PreventAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outResult) override {
CALLHOOK(PreventAttack, {
ctx->SetArgObject(0, (void*)attack);
ctx->SetArgAddress(1, outResult);
})
}
void FailAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outFailed) override {
CALLHOOK(FailAttack, {
ctx->SetArgObject(0, (void*)attack);
ctx->SetArgAddress(1, outFailed);
})
}
void StopBeforeAttack(CreatureLib::Battling::ExecutingAttack* attack, bool* outResult) override {
CALLHOOK(StopBeforeAttack, {
ctx->SetArgObject(0, (void*)attack);
ctx->SetArgAddress(1, outResult);
})
}
void OnBeforeAttack(CreatureLib::Battling::ExecutingAttack* attack) override { Script::OnBeforeAttack(attack); }
void FailIncomingAttack(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target,
bool* outResult) override {
Script::FailIncomingAttack(attack, target, outResult);
}
void IsInvulnerable(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target,
bool* outResult) override {
Script::IsInvulnerable(attack, target, outResult);
}
void OnAttackMiss(CreatureLib::Battling::ExecutingAttack* attack,
CreatureLib::Battling::Creature* target) override {
Script::OnAttackMiss(attack, target);
}
void ChangeAttackType(CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target,
uint8_t hitNumber, uint8_t* outType) override {
Script::ChangeAttackType(attack, target, hitNumber, outType);
}
void OnStatusMove(const CreatureLib::Battling::ExecutingAttack* attack, CreatureLib::Battling::Creature* target,
uint8_t hitNumber) override {
Script::OnStatusMove(attack, target, hitNumber);
}
void PreventSecondaryEffects(const CreatureLib::Battling::ExecutingAttack* attack,
CreatureLib::Battling::Creature* target, uint8_t hitNumber, bool* outResult) override {
Script::PreventSecondaryEffects(attack, target, hitNumber, outResult);
}
void OnSecondaryEffect(const CreatureLib::Battling::ExecutingAttack* attack,
CreatureLib::Battling::Creature* target, uint8_t hitNumber) override {
Script::OnSecondaryEffect(attack, target, hitNumber);
}
void OnAfterHits(const CreatureLib::Battling::ExecutingAttack* attack,
CreatureLib::Battling::Creature* target) override {
Script::OnAfterHits(attack, target);
}
void PreventSelfSwitch(const CreatureLib::Battling::SwitchTurnChoice* choice, bool* outResult) override {
Script::PreventSelfSwitch(choice, outResult);
}
}; };
#undef CALLHOOK
#endif // PKMNLIB_ANGELSCRIPTSCRIPT_HPP #endif // PKMNLIB_ANGELSCRIPTSCRIPT_HPP

View File

@ -2,42 +2,59 @@
#define PKMNLIB_ANGELSCRIPTTYPEINFO_HPP #define PKMNLIB_ANGELSCRIPTTYPEINFO_HPP
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT #define ANGELSCRIPT_DLL_LIBRARY_IMPORT
#include <angelscript.h>
#include <unordered_map>
#include <Core/Exceptions/CreatureException.hpp> #include <Core/Exceptions/CreatureException.hpp>
#include <angelscript.h>
#include <cstring>
#include <unordered_map>
class AngelScriptTypeInfo { class AngelScriptTypeInfo {
private: private:
asITypeInfo* _type = nullptr; asITypeInfo* _type = nullptr;
std::unordered_map<std::string, asIScriptFunction*> _functions; std::unordered_map<std::string, asIScriptFunction*> _functions;
struct FunctionInfo {
bool Exists = false;
asIScriptFunction* Function = nullptr;
};
FunctionInfo Initialize(const std::string& decl) {
auto val = _type->GetMethodByDecl(decl.c_str(), false);
if (val == nullptr){
return FunctionInfo{.Exists = false, .Function = nullptr};
}
if (!val->IsOverride()){
return FunctionInfo{.Exists = false, .Function = nullptr};
}
return FunctionInfo{.Exists = true, .Function = val};
}
public: public:
explicit AngelScriptTypeInfo(asITypeInfo* type) : _type(type){} explicit AngelScriptTypeInfo(asITypeInfo* type) : _type(type) {}
~AngelScriptTypeInfo(){ ~AngelScriptTypeInfo() {
for (const auto& f: _functions){ for (const auto& f : _functions) {
f.second->Release(); f.second->Release();
} }
_functions.clear(); _functions.clear();
} }
asIScriptFunction* GetFunction(const std::string& functionName){ asIScriptFunction* GetFunction(const std::string& functionName) {
auto find = _functions.find(functionName); auto find = _functions.find(functionName);
if (find != _functions.end()){ if (find != _functions.end()) {
return find->second; return find->second;
} }
auto func = _type->GetMethodByName(functionName.c_str()); auto func = _type->GetMethodByName(functionName.c_str());
if (func != nullptr){ if (func != nullptr) {
func->AddRef(); func->AddRef();
} }
_functions.insert({functionName, func}); _functions.insert({functionName, func});
return func; return func;
} }
asIScriptObject* Instantiate(asIScriptContext* ctx){ asIScriptObject* Instantiate(asIScriptContext* ctx) {
auto factory = _type->GetFactoryByIndex(0); auto factory = _type->GetFactoryByIndex(0);
ctx->Prepare(factory); ctx->Prepare(factory);
auto result = ctx->Execute(); auto result = ctx->Execute();
if (result != asEXECUTION_FINISHED){ if (result != asEXECUTION_FINISHED) {
throw CreatureException("Instantiation failed."); throw CreatureException("Instantiation failed.");
} }
asIScriptObject* obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue(); asIScriptObject* obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();
@ -45,6 +62,16 @@ public:
return obj; return obj;
} }
#define SCRIPT_HOOK_FUNCTION(name, decl) \
private: FunctionInfo __##name = Initialize(decl); \
public: const FunctionInfo& Get##name() const { return __##name; }
SCRIPT_HOOK_FUNCTION(Stack, "void Stack()");
SCRIPT_HOOK_FUNCTION(PreventAttack, "void PreventAttack(ExecutingMove@ attack, bool& result)");
SCRIPT_HOOK_FUNCTION(FailAttack, "void FailAttack(ExecutingMove@ attack, bool& result)");
SCRIPT_HOOK_FUNCTION(StopBeforeAttack, "void StopBeforeAttack(ExecutingMove@ attack, bool& result)");
}; };
#undef SCRIPT_HOOK_FUNCTION
#endif // PKMNLIB_ANGELSCRIPTTYPEINFO_HPP #endif // PKMNLIB_ANGELSCRIPTTYPEINFO_HPP

View File

@ -0,0 +1,15 @@
#include "BasicScriptClass.hpp"
#include <cassert>
void BasicScriptClass::Register(asIScriptEngine* engine) {
[[maybe_unused]] int r = engine->GetModuleByIndex(0)->AddScriptSection("PkmnScript", R"(
shared abstract class PkmnScript {
void Stack(){};
void PreventAttack(ExecutingMove@ attack, bool& result){};
void FailAttack(ExecutingMove@ attack, bool& result){};
void StopBeforeAttack(ExecutingMove@ attack, bool& result){};
}
)");
assert(r >= 0);
}

View File

@ -0,0 +1,10 @@
#ifndef PKMNLIB_BASICSCRIPTCLASS_HPP
#define PKMNLIB_BASICSCRIPTCLASS_HPP
#include <angelscript.h>
class BasicScriptClass {
public:
static void Register(asIScriptEngine* engine);
};
#endif // PKMNLIB_BASICSCRIPTCLASS_HPP

View File

@ -10,10 +10,13 @@ void RegisterStaticLibraryTypes::Register(asIScriptEngine* engine) {
void RegisterStaticLibraryTypes::RegisterLibrarySettingsType(asIScriptEngine* engine) { void RegisterStaticLibraryTypes::RegisterLibrarySettingsType(asIScriptEngine* engine) {
[[maybe_unused]] int r = engine->RegisterObjectType("LibrarySettings", 0, asOBJ_REF | asOBJ_NOCOUNT); [[maybe_unused]] int r = engine->RegisterObjectType("LibrarySettings", 0, asOBJ_REF | asOBJ_NOCOUNT);
r = engine->RegisterObjectMethod("LibrarySettings", "uint8 get_MaximalLevel() const property", r = engine->RegisterObjectMethod("LibrarySettings", "uint8 get_MaximalLevel() const property",
asMETHOD(CreatureLib::Library::LibrarySettings, GetMaximalLevel), asCALL_THISCALL); asMETHOD(PkmnLib::Library::LibrarySettings, GetMaximalLevel), asCALL_THISCALL);
assert(r >= 0); assert(r >= 0);
r = engine->RegisterObjectMethod("LibrarySettings", "uint8 get_MaximalMoves() const property", r = engine->RegisterObjectMethod("LibrarySettings", "uint8 get_MaximalMoves() const property",
asMETHOD(CreatureLib::Library::LibrarySettings, GetMaximalMoves), asCALL_THISCALL); asMETHOD(PkmnLib::Library::LibrarySettings, GetMaximalMoves), asCALL_THISCALL);
assert(r >= 0);
r = engine->RegisterObjectMethod("LibrarySettings", "uint16 get_ShinyRate() const property",
asMETHOD(PkmnLib::Library::LibrarySettings, GetShinyRate), asCALL_THISCALL);
assert(r >= 0); assert(r >= 0);
} }

View File

@ -0,0 +1,119 @@
#ifdef TESTS_BUILD
#include "../../extern/catch.hpp"
#include "../../src/Battling/Pokemon/CreatePokemon.hpp"
#include "../../src/ScriptResolving/AngelScript/AngelScripResolver.hpp"
#include "../TestLibrary/TestLibrary.hpp"
#define AS_CLASS(name, contents) { #name, "class " #name " : PkmnScript { " contents "}" }
static std::unordered_map<const char*, const char*> _scripts = std::unordered_map<const char*, const char*> {
AS_CLASS(blankScript, ),
AS_CLASS( stackScript, "int value = 0; void Stack() override { value++; } int GetValue() { return value; }"),
{"doubleInheritanceScript", R"(
class doubleInheritanceScriptBase : PkmnScript {
int value = 0;
void Stack() override{
value++;
}
int GetValue(){ return value; }
}
class doubleInheritanceScript : doubleInheritanceScriptBase {}
)"},
AS_CLASS( preventAttackScript, R"(
void PreventAttack(ExecutingMove@ attack, bool& result) override{
result = !result;
})"),
AS_CLASS( stopBeforeAttackScript, R"(
void StopBeforeAttack(ExecutingMove@ attack, bool& result) override{
result = !result;
})"),
};
static const char* _testLoadFunc(const char* name) { return _scripts[name]; }
static AngelScripResolver* _resolverCache = nullptr;
static AngelScripResolver* GetScriptResolver(PkmnLib::Battling::BattleLibrary* mainLib) {
if (_resolverCache == nullptr) {
_resolverCache = dynamic_cast<AngelScripResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
_resolverCache->Initialize(mainLib);
_resolverCache->SetCreateFunction(&_testLoadFunc);
for (auto kv : _scripts) {
_resolverCache->CreateScript(kv.first);
}
_resolverCache->FinalizeModule();
}
return _resolverCache;
}
static AngelScriptScript* GetScript(PkmnLib::Battling::BattleLibrary* mainLib, const char* scriptName) {
auto lib = GetScriptResolver(mainLib);
auto s = lib->LoadScript(AngelScripResolver::ScriptCategory::Creature, scriptName);
auto script = dynamic_cast<AngelScriptScript*>(s);
return script;
}
TEST_CASE("Invoke non-implemented script function") {
auto mainLib = TestLibrary::GetLibrary();
auto script = GetScript(mainLib, "blankScript");
script->Stack();
delete script;
}
TEST_CASE("Invoke Stack script function") {
auto mainLib = TestLibrary::GetLibrary();
auto script = GetScript(mainLib, "stackScript");
for (int i = 1; i <= 10; i++) {
script->Stack();
auto ctxPool = script->GetContextPool();
auto ctx = ctxPool->RequestContext();
script->PrepareMethod("GetValue", ctx);
REQUIRE(ctx->Execute() == asEXECUTION_FINISHED);
REQUIRE(ctx->GetReturnDWord() == i);
ctxPool->ReturnContextToPool(ctx);
}
delete script;
}
TEST_CASE("Invoke Stack script function with implementation in base class") {
auto mainLib = TestLibrary::GetLibrary();
auto script = GetScript(mainLib, "doubleInheritanceScript");
for (int i = 1; i <= 10; i++) {
script->Stack();
auto ctxPool = script->GetContextPool();
auto ctx = ctxPool->RequestContext();
script->PrepareMethod("GetValue", ctx);
REQUIRE(ctx->Execute() == asEXECUTION_FINISHED);
REQUIRE(ctx->GetReturnDWord() == i);
ctxPool->ReturnContextToPool(ctx);
}
delete script;
}
TEST_CASE("Invoke preventAttackScript script function") {
auto mainLib = TestLibrary::GetLibrary();
auto script = GetScript(mainLib, "preventAttackScript");
bool b = false;
script->PreventAttack(nullptr, &b);
REQUIRE(b);
delete script;
}
TEST_CASE("Invoke StopBeforeAttack script function") {
auto mainLib = TestLibrary::GetLibrary();
auto script = GetScript(mainLib, "stopBeforeAttackScript");
bool b = false;
script->StopBeforeAttack(nullptr, &b);
REQUIRE(b);
delete script;
}
#endif

View File

@ -89,41 +89,4 @@ TEST_CASE("Build script resolver, create object, invoke addition method") {
delete lib; delete lib;
} }
TEST_CASE("Test whether species object was properly registered.") {
auto lib = dynamic_cast<AngelScripResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
auto mainLib = TestLibrary::GetLibrary();
lib->Initialize(mainLib);
lib->SetCreateFunction(&_testLoadFunc);
lib->CreateScript("testScript1");
lib->FinalizeModule();
auto species = mainLib->GetSpeciesLibrary()->GetPkmnSpecies("testSpecies2");
auto obj =
dynamic_cast<AngelScriptScript*>(lib->LoadScript(AngelScripResolver::ScriptCategory::Creature, "testScript1"));
auto ctxPool = obj->GetContextPool();
auto ctx = ctxPool->RequestContext();
obj->PrepareMethod("testFunc2", ctx);
ctx->SetArgObject(0, const_cast<PkmnLib::Library::PokemonSpecies*>(species));
auto result = ctx->Execute();
if (result == asEXECUTION_EXCEPTION){
FAIL(ctx->GetExceptionString());
}
ctx->Unprepare();
species = mainLib->GetSpeciesLibrary()->GetPkmnSpecies("testSpecies");
obj->PrepareMethod("testFunc2", ctx);
ctx->SetArgObject(0, const_cast<PkmnLib::Library::PokemonSpecies*>(species));
result = ctx->Execute();
REQUIRE(result == asEXECUTION_EXCEPTION);
ctxPool->ReturnContextToPool(ctx);
delete obj;
delete lib;
}
#endif #endif