#include "AngelScripResolver.hpp" #include #include #include #include "../../../extern/angelscript_addons/scriptarray/scriptarray.h" #include "../../../extern/angelscript_addons/scripthandle/scripthandle.h" #include "../../../extern/angelscript_addons/scripthelper/scripthelper.h" #include "../../../extern/angelscript_addons/scriptstdstring/scriptstdstring.h" #include "ByteCodeHandling/FileByteCodeStream.hpp" #include "ByteCodeHandling/MemoryByteCodeStream.hpp" #include "TypeRegistry/BasicScriptClass.hpp" #include "TypeRegistry/Battling/RegisterBattleClass.hpp" #include "TypeRegistry/Battling/RegisterBattleLibrary.hpp" #include "TypeRegistry/Battling/RegisterExecutingAttack.hpp" #include "TypeRegistry/Battling/RegisterPokemonClass.hpp" #include "TypeRegistry/Battling/RegisterTurnChoices.hpp" #include "TypeRegistry/ConstString.hpp" #include "TypeRegistry/Library/RegisterEffectParameter.hpp" #include "TypeRegistry/Library/RegisterGrowthRateTypes.hpp" #include "TypeRegistry/Library/RegisterItemTypes.hpp" #include "TypeRegistry/Library/RegisterMoveTypes.hpp" #include "TypeRegistry/Library/RegisterSpeciesTypes.hpp" #include "TypeRegistry/Library/RegisterStaticLibraryTypes.hpp" #include "TypeRegistry/Library/RegisterTypeLibrary.hpp" CreatureLib::Battling::ScriptResolver* PkmnLib::Battling::BattleLibrary::CreateScriptResolver() { return new AngelScripResolver(); } static void TranslateException(asIScriptContext* ctx, void* /*userParam*/) { try { // Retrow the original exception so we can catch it again throw; } catch (std::exception& e) { // Tell the VM the type of exception that occurred ctx->SetException(e.what()); } catch (...) { // The callback must not allow any exception to be thrown, but it is not necessary // to explicitly set an exception string if the default exception string is sufficient } } void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) { for (auto scriptCategory : ScriptCategoryHelper::GetValues()) { _typeDatabase.Insert(scriptCategory, {}); } auto library = (PkmnLib::Battling::BattleLibrary*)arg; _engine = asCreateScriptEngine(); _engine->SetTranslateAppExceptionCallback(asFUNCTION(TranslateException), 0, asCALL_CDECL); int32_t r = _engine->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL); if (r < 0) throw CreatureException("Registering message callback failed."); _engine->SetEngineProperty(asEP_DISALLOW_EMPTY_LIST_ELEMENTS, 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_AUTO_GARBAGE_COLLECT, false); _engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, true); _engine->SetEngineProperty(asEP_PROPERTY_ACCESSOR_MODE, 2); _engine->SetEngineProperty(asEP_COMPILER_WARNINGS, 2); RegisterStdString(_engine); ConstStringRegister::Register(_engine); // Register Script Array type RegisterScriptArray(_engine, true); r = _engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(Print), asCALL_CDECL); if (r < 0) throw CreatureException("Registering print function failed."); RegisterScriptHandle(_engine); _builder.StartNewModule(_engine, "pkmn"); _mainModule = _builder.GetModule(); RegisterTypes(); RegisterExceptionRoutines(_engine); auto staticLib = library->GetStaticLib(); _engine->RegisterGlobalProperty("const StaticLibrary@ StaticLib", &staticLib); _contextPool = new ContextPool(_engine); } void AngelScripResolver::RegisterTypes() { // Register static library types RegisterSpeciesTypes::Register(_engine); RegisterItemTypes::Register(_engine); RegisterMoveTypes::Register(_engine); RegisterGrowthRateTypes::Register(_engine); RegisterTypeLibrary::Register(_engine); RegisterStaticLibraryTypes::Register(_engine); RegisterEffectParameter::Register(_engine); // Register battle types RegisterPokemonClass::Register(_engine); RegisterExecutingAttack::Register(_engine); RegisterTurnChoices::Register(_engine); RegisterBattleLibrary::Register(_engine); RegisterBattleClass::Register(_engine); [[maybe_unused]] int r = _engine->RegisterObjectMethod("Pokemon", "const Battle@ get_Battle() const property", asMETHOD(CreatureLib::Battling::Creature, GetBattle), asCALL_THISCALL); assert(r >= 0); // Register base script BasicScriptClass::Register(_engine); } void AngelScripResolver::MessageCallback(const asSMessageInfo* msg, void* param) { const char* type = "ERR "; if (msg->type == asMSGTYPE_WARNING) type = "WARN"; else if (msg->type == asMSGTYPE_INFORMATION) type = "INFO"; printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message); } CreatureLib::Battling::Script* AngelScripResolver::LoadScript(ScriptCategory category, const ConstString& scriptName) { Dictionary innerDb; if (!_typeDatabase.TryGet(category, innerDb)) { _typeDatabase.Insert(category, innerDb); return nullptr; } AngelScriptTypeInfo* t; if (!innerDb.TryGet(scriptName, t)) { innerDb.Insert(scriptName, nullptr); return nullptr; } if (t == nullptr) { return nullptr; } auto ctx = _contextPool->RequestContext(); auto obj = t->Instantiate(ctx); _contextPool->ReturnContextToPool(ctx); return new AngelScriptScript(t, obj, _contextPool); } void AngelScripResolver::FinalizeModule() { int r = _builder.BuildModule(); if (r < 0) throw CreatureException("Building Script Module failed."); asUINT count = _mainModule->GetObjectTypeCount(); std::regex metadataMatcher("^\\s*(\\w+)([\\w\\s=]*)$", std::regex_constants::icase); std::regex variableMatcher("\\s*(\\w+)=(\\w+)", std::regex_constants::icase); std::smatch base_match; for (asUINT n = 0; n < count; n++) { auto typeInfo = _mainModule->GetObjectTypeByIndex(n); auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId()); for (size_t m = 0; m < metadata.size(); m++) { auto data = metadata[m]; if (std::regex_match(data, base_match, metadataMatcher)) { auto metadataKind = base_match[1].str(); auto metadataVariables = base_match[2].str(); if (!std::regex_match(metadataVariables, base_match, variableMatcher)) { continue; } ConstString effectName; for (size_t variableIndex = 1; variableIndex < base_match.size(); variableIndex += 2) { if (base_match[variableIndex] == "effect") { auto val = base_match[variableIndex + 1].str(); effectName = ConstString(val); } } if (effectName.Empty()) { continue; } if (metadataKind == "Move") { _typeDatabase[ScriptCategory::Attack].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo)); } else if (metadataKind == "Pokemon") { _typeDatabase[ScriptCategory::Creature].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo)); } else if (metadataKind == "Ability") { _typeDatabase[ScriptCategory::Talent].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo)); } else if (metadataKind == "Status") { _typeDatabase[ScriptCategory::Status].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo)); } else if (metadataKind == "Battle") { _typeDatabase[ScriptCategory::Battle].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo)); } else if (metadataKind == "Side") { _typeDatabase[ScriptCategory::Side].Insert(effectName, new AngelScriptTypeInfo(effectName, typeInfo)); } } } } } void AngelScripResolver::CreateScript(const char* name, const char* script) { _builder.AddSectionFromMemory(name, script); } void AngelScripResolver::WriteByteCodeToFile(const char* file, bool stripDebugInfo) { FILE* wFile = fopen(file, "w"); auto stream = new FileByteCodeStream(wFile); _mainModule->SaveByteCode(stream, stripDebugInfo); fclose(wFile); delete stream; } void AngelScripResolver::LoadByteCodeFromFile( const char* file, const Dictionary>& types) { FILE* rFile = fopen(file, "r"); auto stream = new FileByteCodeStream(rFile); LoadByteCode(stream, types); fclose(rFile); delete stream; //_typeDatabase = types; } uint8_t* AngelScripResolver::WriteByteCodeToMemory(size_t& size, bool stripDebugInfo) { auto stream = new MemoryByteCodeStream(); auto result = _mainModule->SaveByteCode(stream, stripDebugInfo); Assert(result == asSUCCESS); auto arr = stream->GetOut(); size = stream->GetWrittenSize(); arr = static_cast(realloc(arr, size * sizeof(uint8_t))); delete stream; return arr; } void AngelScripResolver::LoadByteCodeFromMemory( uint8_t* byte, size_t size, const Dictionary>& types) { auto stream = new MemoryByteCodeStream(byte, size); LoadByteCode(stream, types); delete stream; } void AngelScripResolver::LoadByteCode(asIBinaryStream* stream, const Dictionary>& types) { int result = _mainModule->LoadByteCode(stream); Assert(result == asSUCCESS); auto typeCount = _mainModule->GetObjectTypeCount(); Dictionary objectTypes; for (asUINT i = 0; i < typeCount; i++) { auto t = _mainModule->GetObjectTypeByIndex(i); objectTypes.Insert(ConstString::GetHash(t->GetName()), t); } Dictionary> typeDatabase; for (auto& innerDb : types) { Dictionary newInnerDb; for (auto& val : innerDb.second) { auto decl = val.second; auto type = objectTypes[ConstString::GetHash(decl)]; newInnerDb.Insert(val.first, new AngelScriptTypeInfo(val.first, type)); } typeDatabase.Insert(innerDb.first, newInnerDb); } _typeDatabase = typeDatabase; }