Support for saving compiled AngelScript to either file or RAM, so we can reuse it.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
0b045db811
commit
bf36103c11
|
@ -6,6 +6,8 @@
|
||||||
#include "../../../extern/angelscript_addons/scripthandle/scripthandle.h"
|
#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 "ByteCodeHandling/FileByteCodeStream.hpp"
|
||||||
|
#include "ByteCodeHandling/MemoryByteCodeStream.hpp"
|
||||||
#include "TypeRegistry/BasicScriptClass.hpp"
|
#include "TypeRegistry/BasicScriptClass.hpp"
|
||||||
#include "TypeRegistry/Battling/RegisterBattleClass.hpp"
|
#include "TypeRegistry/Battling/RegisterBattleClass.hpp"
|
||||||
#include "TypeRegistry/Battling/RegisterBattleLibrary.hpp"
|
#include "TypeRegistry/Battling/RegisterBattleLibrary.hpp"
|
||||||
|
@ -25,26 +27,19 @@ CreatureLib::Battling::ScriptResolver* PkmnLib::Battling::BattleLibrary::CreateS
|
||||||
return new AngelScripResolver();
|
return new AngelScripResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TranslateException(asIScriptContext *ctx, void* /*userParam*/)
|
static void TranslateException(asIScriptContext* ctx, void* /*userParam*/) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
// Retrow the original exception so we can catch it again
|
// Retrow the original exception so we can catch it again
|
||||||
throw;
|
throw;
|
||||||
}
|
} catch (std::exception& e) {
|
||||||
catch( std::exception &e )
|
|
||||||
{
|
|
||||||
// Tell the VM the type of exception that occurred
|
// Tell the VM the type of exception that occurred
|
||||||
ctx->SetException(e.what());
|
ctx->SetException(e.what());
|
||||||
}
|
} catch (...) {
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
// The callback must not allow any exception to be thrown, but it is not necessary
|
// 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
|
// to explicitly set an exception string if the default exception string is sufficient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) {
|
void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) {
|
||||||
for (auto scriptCategory : ScriptCategoryHelper::GetValues()) {
|
for (auto scriptCategory : ScriptCategoryHelper::GetValues()) {
|
||||||
_typeDatabase.Insert(scriptCategory, {});
|
_typeDatabase.Insert(scriptCategory, {});
|
||||||
|
@ -125,7 +120,7 @@ void AngelScripResolver::MessageCallback(const asSMessageInfo* msg, void* param)
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureLib::Battling::Script* AngelScripResolver::LoadScript(ScriptCategory category, const ConstString& scriptName) {
|
CreatureLib::Battling::Script* AngelScripResolver::LoadScript(ScriptCategory category, const ConstString& scriptName) {
|
||||||
Dictionary<uint32_t, AngelScriptTypeInfo*> innerDb;
|
Dictionary<ConstString, AngelScriptTypeInfo*> innerDb;
|
||||||
if (!_typeDatabase.TryGet(category, innerDb)) {
|
if (!_typeDatabase.TryGet(category, innerDb)) {
|
||||||
_typeDatabase.Insert(category, innerDb);
|
_typeDatabase.Insert(category, innerDb);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -200,3 +195,58 @@ void AngelScripResolver::FinalizeModule() {
|
||||||
void AngelScripResolver::CreateScript(const char* name, const char* script) {
|
void AngelScripResolver::CreateScript(const char* name, const char* script) {
|
||||||
_builder.AddSectionFromMemory(name, 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<ScriptCategory, Dictionary<ConstString, const char*>>& 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<uint8_t*>(realloc(arr, size));
|
||||||
|
delete stream;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
void AngelScripResolver::LoadByteCodeFromMemory(
|
||||||
|
uint8_t* byte, size_t size, const Dictionary<ScriptCategory, Dictionary<ConstString, const char*>>& types) {
|
||||||
|
auto stream = new MemoryByteCodeStream(byte, size);
|
||||||
|
LoadByteCode(stream, types);
|
||||||
|
delete stream;
|
||||||
|
}
|
||||||
|
void AngelScripResolver::LoadByteCode(asIBinaryStream* stream,
|
||||||
|
const Dictionary<ScriptCategory, Dictionary<ConstString, const char*>>& types) {
|
||||||
|
int result = _mainModule->LoadByteCode(stream);
|
||||||
|
Assert(result == asSUCCESS);
|
||||||
|
|
||||||
|
auto typeCount = _mainModule->GetObjectTypeCount();
|
||||||
|
Dictionary<uint32_t, asITypeInfo*> objectTypes;
|
||||||
|
for (asUINT i = 0; i < typeCount; i++) {
|
||||||
|
auto t = _mainModule->GetObjectTypeByIndex(i);
|
||||||
|
objectTypes.Insert(ConstString::GetHash(t->GetName()), t);
|
||||||
|
}
|
||||||
|
Dictionary<ScriptCategory, Dictionary<ConstString, AngelScriptTypeInfo*>> typeDatabase;
|
||||||
|
for (auto& innerDb : types) {
|
||||||
|
Dictionary<ConstString, AngelScriptTypeInfo*> 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;
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
#define PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
#define PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
||||||
|
|
||||||
#include <CreatureLib/Battling/ScriptHandling/ScriptResolver.hpp>
|
#include <CreatureLib/Battling/ScriptHandling/ScriptResolver.hpp>
|
||||||
#include "../../Battling/Library/BattleLibrary.hpp"
|
|
||||||
#include "../../../extern/angelscript_addons/scriptbuilder/scriptbuilder.h"
|
#include "../../../extern/angelscript_addons/scriptbuilder/scriptbuilder.h"
|
||||||
|
#include "../../Battling/Library/BattleLibrary.hpp"
|
||||||
|
|
||||||
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT
|
#define ANGELSCRIPT_DLL_LIBRARY_IMPORT
|
||||||
#include <angelscript.h>
|
#include <angelscript.h>
|
||||||
|
@ -21,9 +21,11 @@ private:
|
||||||
|
|
||||||
static void MessageCallback(const asSMessageInfo* msg, void* param);
|
static void MessageCallback(const asSMessageInfo* msg, void* param);
|
||||||
static void Print(const std::string& str) { std::cout << str << std::endl; }
|
static void Print(const std::string& str) { std::cout << str << std::endl; }
|
||||||
Dictionary<ScriptCategory, Dictionary<uint32_t, AngelScriptTypeInfo*>> _typeDatabase;
|
Dictionary<ScriptCategory, Dictionary<ConstString, AngelScriptTypeInfo*>> _typeDatabase;
|
||||||
|
|
||||||
void RegisterTypes();
|
void RegisterTypes();
|
||||||
|
void LoadByteCode(asIBinaryStream* stream,
|
||||||
|
const Dictionary<ScriptCategory, Dictionary<ConstString, const char*>>& types);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~AngelScripResolver() override {
|
~AngelScripResolver() override {
|
||||||
|
@ -42,5 +44,16 @@ public:
|
||||||
void FinalizeModule();
|
void FinalizeModule();
|
||||||
|
|
||||||
CreatureLib::Battling::Script* LoadScript(ScriptCategory category, const ConstString& scriptName) override;
|
CreatureLib::Battling::Script* LoadScript(ScriptCategory category, const ConstString& scriptName) override;
|
||||||
|
|
||||||
|
void WriteByteCodeToFile(const char* file, bool stripDebugInfo = false);
|
||||||
|
void LoadByteCodeFromFile(const char* file,
|
||||||
|
const Dictionary<ScriptCategory, Dictionary<ConstString, const char*>>& types);
|
||||||
|
uint8_t* WriteByteCodeToMemory(size_t& size, bool stripDebugInfo = false);
|
||||||
|
void LoadByteCodeFromMemory(uint8_t*, size_t size,
|
||||||
|
const Dictionary<ScriptCategory, Dictionary<ConstString, const char*>>& types);
|
||||||
|
|
||||||
|
const Dictionary<ScriptCategory, Dictionary<ConstString, AngelScriptTypeInfo*>>& GetTypeDatabase() const noexcept {
|
||||||
|
return _typeDatabase;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
#endif // PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
#endif // PKMNLIB_ANGELSCRIPRESOLVER_HPP
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
|
|
||||||
const ConstString& GetName() const noexcept { return _name; }
|
const ConstString& GetName() const noexcept { return _name; }
|
||||||
|
|
||||||
|
const char* GetDecl(){
|
||||||
|
return _type->GetName();
|
||||||
|
}
|
||||||
|
|
||||||
asIScriptFunction* GetFunction(const ConstString& functionName) {
|
asIScriptFunction* GetFunction(const ConstString& functionName) {
|
||||||
asIScriptFunction* func;
|
asIScriptFunction* func;
|
||||||
if (_functions.TryGet(functionName, func)) {
|
if (_functions.TryGet(functionName, func)) {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef PKMNLIB_FILEBYTECODESTREAM_HPP
|
||||||
|
#define PKMNLIB_FILEBYTECODESTREAM_HPP
|
||||||
|
#include <angelscript.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
class FileByteCodeStream : public asIBinaryStream {
|
||||||
|
private:
|
||||||
|
FILE* _file;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FileByteCodeStream(FILE* file) : _file(file) {}
|
||||||
|
|
||||||
|
int Write(const void* ptr, asUINT size) {
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
return fwrite(ptr, size, 1, _file);
|
||||||
|
}
|
||||||
|
int Read(void* ptr, asUINT size) {
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
return fread(ptr, size, 1, _file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PKMNLIB_FILEBYTECODESTREAM_HPP
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef PKMNLIB_MEMORYBYTECODESTREAM_HPP
|
||||||
|
#define PKMNLIB_MEMORYBYTECODESTREAM_HPP
|
||||||
|
#include <angelscript.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class MemoryByteCodeStream : public asIBinaryStream {
|
||||||
|
private:
|
||||||
|
uint8_t* _out;
|
||||||
|
size_t _index = 0;
|
||||||
|
size_t _size = 0;
|
||||||
|
size_t _capacity = 0;
|
||||||
|
|
||||||
|
#define MEM_STEPS 256
|
||||||
|
|
||||||
|
public:
|
||||||
|
MemoryByteCodeStream() : _out((uint8_t*)malloc(MEM_STEPS * sizeof(uint8_t) )), _capacity(MEM_STEPS){};
|
||||||
|
MemoryByteCodeStream(uint8_t* in, size_t size) : _out(in), _size(size) {}
|
||||||
|
|
||||||
|
uint8_t* GetOut() const { return _out; }
|
||||||
|
size_t GetWrittenSize() const { return _size; }
|
||||||
|
|
||||||
|
int Write(const void* ptr, asUINT size) final {
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
auto initialSize = _size;
|
||||||
|
if (_size + size >= _capacity) {
|
||||||
|
_capacity += MEM_STEPS;
|
||||||
|
auto newLoc = realloc(_out, _capacity * sizeof(uint8_t));
|
||||||
|
if (newLoc == nullptr) {
|
||||||
|
throw CreatureException("Out of memory.");
|
||||||
|
}
|
||||||
|
_out = (uint8_t*)newLoc;
|
||||||
|
}
|
||||||
|
auto start = reinterpret_cast<const uint8_t*>(ptr);
|
||||||
|
for (auto index = 0; index < size; index++) {
|
||||||
|
_out[initialSize + index] = *(start + index);
|
||||||
|
}
|
||||||
|
_size += size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
int Read(void* ptr, asUINT size) final {
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
auto start = reinterpret_cast<uint8_t*>(ptr);
|
||||||
|
auto toRead = size;
|
||||||
|
if (_index + toRead > _size) {
|
||||||
|
toRead = _size - _index;
|
||||||
|
}
|
||||||
|
for (auto index = 0; index < toRead; index++) {
|
||||||
|
*(start + index) = _out[_index + index];
|
||||||
|
}
|
||||||
|
_index += toRead;
|
||||||
|
return toRead;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#undef MEM_STEPS
|
||||||
|
#endif // PKMNLIB_MEMORYBYTECODESTREAM_HPP
|
|
@ -79,4 +79,68 @@ TEST_CASE("Build script resolver, create object, invoke addition method") {
|
||||||
delete lib;
|
delete lib;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Get a script resolver, save the byte code to memory, create new script resolver from it") {
|
||||||
|
auto originLib = dynamic_cast<AngelScripResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||||
|
originLib->Initialize(TestLibrary::GetLibrary());
|
||||||
|
originLib->CreateScript("testScript1" , _scripts["testScript1"]);
|
||||||
|
originLib->FinalizeModule();
|
||||||
|
size_t size;
|
||||||
|
auto byteCode = originLib->WriteByteCodeToMemory(size);
|
||||||
|
auto typeDatabase = originLib->GetTypeDatabase();
|
||||||
|
|
||||||
|
Dictionary<ScriptCategory, Dictionary<ConstString, const char*>> types;
|
||||||
|
for (auto& innerDb: typeDatabase){
|
||||||
|
Dictionary<ConstString, const char*> newInnerDb;
|
||||||
|
for (auto& kv : innerDb.second){
|
||||||
|
INFO(kv.second->GetDecl());
|
||||||
|
newInnerDb.Insert(kv.first, kv.second->GetDecl());
|
||||||
|
}
|
||||||
|
types.Insert(innerDb.first, newInnerDb);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newLib = dynamic_cast<AngelScripResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||||
|
newLib->Initialize(TestLibrary::GetLibrary());
|
||||||
|
newLib->LoadByteCodeFromMemory(byteCode, size, types);
|
||||||
|
auto obj =
|
||||||
|
dynamic_cast<AngelScriptScript*>(newLib->LoadScript(ScriptCategory::Creature, "testScript1"_cnc));
|
||||||
|
REQUIRE(obj != nullptr);
|
||||||
|
delete obj;
|
||||||
|
delete originLib;
|
||||||
|
delete newLib;
|
||||||
|
free(byteCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Get a script resolver, save the byte code to file, create new script resolver from it") {
|
||||||
|
const char* TestFileName = "compiledAngelScriptTestFile.bin";
|
||||||
|
auto originLib = dynamic_cast<AngelScripResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||||
|
originLib->Initialize(TestLibrary::GetLibrary());
|
||||||
|
originLib->CreateScript("testScript1" , _scripts["testScript1"]);
|
||||||
|
originLib->FinalizeModule();
|
||||||
|
originLib->WriteByteCodeToFile(TestFileName);
|
||||||
|
auto typeDatabase = originLib->GetTypeDatabase();
|
||||||
|
|
||||||
|
Dictionary<ScriptCategory, Dictionary<ConstString, const char*>> types;
|
||||||
|
for (auto& innerDb: typeDatabase){
|
||||||
|
Dictionary<ConstString, const char*> newInnerDb;
|
||||||
|
for (auto& kv : innerDb.second){
|
||||||
|
INFO(kv.second->GetDecl());
|
||||||
|
newInnerDb.Insert(kv.first, kv.second->GetDecl());
|
||||||
|
}
|
||||||
|
types.Insert(innerDb.first, newInnerDb);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newLib = dynamic_cast<AngelScripResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||||
|
newLib->Initialize(TestLibrary::GetLibrary());
|
||||||
|
newLib->LoadByteCodeFromFile(TestFileName, types);
|
||||||
|
auto obj =
|
||||||
|
dynamic_cast<AngelScriptScript*>(newLib->LoadScript(ScriptCategory::Creature, "testScript1"_cnc));
|
||||||
|
REQUIRE(obj != nullptr);
|
||||||
|
delete obj;
|
||||||
|
delete originLib;
|
||||||
|
delete newLib;
|
||||||
|
remove(TestFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
Loading…
Reference in New Issue