feat(lua): added basis for lua scripting

This commit is contained in:
maelstrom 2025-04-24 17:08:22 +02:00
parent 4cfb327b65
commit c081469a49
17 changed files with 164 additions and 14 deletions

View file

@ -11,11 +11,15 @@ find_package(pugixml 1.15 REQUIRED)
find_package(Stb REQUIRED)
include_directories(${Stb_INCLUDE_DIR})
# PkgConfig packages
find_package(PkgConfig REQUIRED)
pkg_check_modules(LUAJIT REQUIRED luajit)
file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h")
add_library(openblocks STATIC ${SOURCES})
set_target_properties(openblocks PROPERTIES OUTPUT_NAME "openblocks")
target_link_libraries(openblocks ${GLEW_LIBRARIES} OpenGL::GL ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
target_include_directories(openblocks PUBLIC "src" "../include")
target_link_libraries(openblocks ${GLEW_LIBRARIES} ${LUAJIT_LIBRARIES} OpenGL::GL ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
target_include_directories(openblocks PUBLIC "src" "../include" ${LUAJIT_INCLUDE_DIR})
# Windows-specific dependencies
if(WIN32)

7
core/src/lua.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
extern "C" {
#include <luajit-2.1/luajit.h>
#include <luajit-2.1/lauxlib.h>
#include <luajit-2.1/lualib.h>
#include <luajit-2.1/lua.h>
}

View file

@ -25,9 +25,11 @@ class Workspace;
typedef int InstanceFlags;
// This instance should only be instantiated in special circumstances (i.e. by DataModel) and should be creatable directly via any API
const InstanceFlags INSTANCE_NOTCREATABLE = (InstanceFlags)0x1;
const InstanceFlags INSTANCE_NOTCREATABLE = (InstanceFlags)1<<0;
// This instance is a service
const InstanceFlags INSTANCE_SERVICE = (InstanceFlags)0x2;
const InstanceFlags INSTANCE_SERVICE = (InstanceFlags)1<<1;
// This instance should be hidden from the explorer
const InstanceFlags INSTANCE_HIDDEN = (InstanceFlags)1<<2;
// Struct describing information about an instance
struct InstanceType {

View file

@ -17,4 +17,7 @@ void Service::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent
}
void Service::InitService() {
}
void Service::OnRun() {
}

View file

@ -8,6 +8,7 @@ class Service : public Instance {
protected:
Service(const InstanceType* type);
virtual void InitService();
virtual void OnRun();
void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) override;

View file

@ -4,6 +4,7 @@
#include "objects/base/refstate.h"
#include "objects/base/service.h"
#include "objects/meta.h"
#include "objects/script/serverscriptservice.h"
#include "workspace.h"
#include "logger.h"
#include "panic.h"
@ -27,16 +28,19 @@ DataModel::DataModel()
this->name = "Place";
}
void DataModel::Init() {
void DataModel::Init(bool runMode) {
// Create the workspace if it doesn't exist
if (this->services.count("Workspace") == 0) {
this->services["Workspace"] = std::make_shared<Workspace>();
AddChild(this->services["Workspace"]);
}
GetService<ServerScriptService>();
// Init all services
for (auto [_, service] : this->services) {
service->InitService();
if (runMode) service->OnRun();
}
}
@ -128,7 +132,7 @@ result<std::shared_ptr<Service>, NoSuchService> DataModel::GetService(std::strin
if (services.count(className) != 0)
return std::dynamic_pointer_cast<Service>(services[className]);
if (!INSTANCE_MAP[className] || (INSTANCE_MAP[className]->flags ^ (INSTANCE_NOTCREATABLE | INSTANCE_SERVICE)) != 0) {
if (!INSTANCE_MAP[className] || ~(INSTANCE_MAP[className]->flags & (INSTANCE_NOTCREATABLE | INSTANCE_SERVICE)) == 0) {
return NoSuchService(className);
}

View file

@ -24,7 +24,7 @@ public:
std::optional<std::string> currentFile;
DataModel();
void Init();
void Init(bool runMode = false);
static inline std::shared_ptr<DataModel> New() { return std::make_shared<DataModel>(); };
virtual const InstanceType* GetClass() override;

View file

@ -6,7 +6,7 @@ const InstanceType JointsService::TYPE = {
.super = &Instance::TYPE,
.className = "JointsService",
.constructor = &JointsService::Create,
.flags = INSTANCE_NOTCREATABLE | INSTANCE_SERVICE,
.flags = INSTANCE_NOTCREATABLE | INSTANCE_SERVICE | INSTANCE_HIDDEN,
};
const InstanceType* JointsService::GetClass() {

View file

@ -4,15 +4,23 @@
#include "objects/part.h"
#include "objects/joint/snap.h"
#include "objects/script.h"
#include "objects/script/scriptcontext.h"
#include "objects/script/serverscriptservice.h"
#include "objects/workspace.h"
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
{ "Instance", &Instance::TYPE },
{ "Part", &Part::TYPE },
{ "Workspace", &Workspace::TYPE },
{ "DataModel", &DataModel::TYPE },
{ "Part", &Part::TYPE },
{ "Snap", &Snap::TYPE },
{ "JointInstance", &JointInstance::TYPE },
{ "JointsService", &JointsService::TYPE },
{ "Script", &Script::TYPE },
// Services
{ "Workspace", &Workspace::TYPE },
{ "JointsService", &JointsService::TYPE },
{ "ScriptContext", &ScriptContext::TYPE },
{ "ServerScriptService", &ServerScriptService::TYPE },
};

View file

@ -28,4 +28,12 @@ Script::Script(): Instance(&TYPE) {
}
Script::~Script() {
}
void Script::Run() {
}
void Script::Stop() {
// TODO:
}

View file

@ -11,6 +11,8 @@ public:
~Script();
std::string source;
void Run();
void Stop();
static inline std::shared_ptr<Script> New() { return std::make_shared<Script>(); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Script>(); };

View file

@ -0,0 +1,29 @@
#include "scriptcontext.h"
#include <luajit-2.1/lauxlib.h>
#include <luajit-2.1/lua.h>
const InstanceType ScriptContext::TYPE = {
.super = &Instance::TYPE,
.className = "ScriptContext",
.constructor = &ScriptContext::Create,
.flags = INSTANCE_NOTCREATABLE | INSTANCE_SERVICE | INSTANCE_HIDDEN,
};
const InstanceType* ScriptContext::GetClass() {
return &TYPE;
}
ScriptContext::ScriptContext(): Service(&TYPE) {
}
ScriptContext::~ScriptContext() {
if (state)
lua_close(state);
}
void ScriptContext::InitService() {
if (initialized) return;
initialized = true;
state = luaL_newstate();
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "objects/base/service.h"
#include "lua.h"
class ScriptContext : public Service {
private:
protected:
void InitService() override;
bool initialized = false;
public:
const static InstanceType TYPE;
ScriptContext();
~ScriptContext();
lua_State* state;
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ScriptContext>(); };
virtual const InstanceType* GetClass() override;
};

View file

@ -0,0 +1,37 @@
#include "serverscriptservice.h"
#include "objects/script.h"
#include "objects/workspace.h"
const InstanceType ServerScriptService::TYPE = {
.super = &Instance::TYPE,
.className = "ServerScriptService",
.constructor = &ServerScriptService::Create,
.flags = INSTANCE_NOTCREATABLE | INSTANCE_SERVICE,
};
const InstanceType* ServerScriptService::GetClass() {
return &TYPE;
}
ServerScriptService::ServerScriptService(): Service(&TYPE) {
}
ServerScriptService::~ServerScriptService() = default;
void ServerScriptService::InitService() {
if (initialized) return;
initialized = true;
}
void Service::OnRun() {
auto workspace = dataModel().value()->GetService<Workspace>();
for (auto it = workspace->GetDescendantsStart(); it != workspace->GetDescendantsEnd(); it++) {
if (!it->IsA<Script>()) continue;
it->CastTo<Script>().expect()->Run();
}
for (auto it = GetDescendantsStart(); it != GetDescendantsEnd(); it++) {
if (!it->IsA<Script>()) continue;
it->CastTo<Script>().expect()->Run();
}
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "objects/base/service.h"
// Container class for server scripts
// Also handles/manages running server scripts on run
class ServerScriptService : public Service {
private:
protected:
void InitService() override;
void OnRun() override;
bool initialized = false;
public:
const static InstanceType TYPE;
ServerScriptService();
~ServerScriptService();
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ServerScriptService>(); };
virtual const InstanceType* GetClass() override;
};

View file

@ -1,5 +1,7 @@
#include "explorermodel.h"
#include "common.h"
#include "objects/base/instance.h"
#include "objects/base/member.h"
#include <qicon.h>
#include <qmimedata.h>
#include <QWidget>
@ -43,7 +45,7 @@ QModelIndex ExplorerModel::index(int row, int column, const QModelIndex &parent)
? static_cast<Instance*>(parent.internalPointer())
: rootItem.get();
if (parentItem->GetChildren().size() >= row)
if (parentItem->GetChildren().size() >= row && !(parentItem->GetChildren()[row]->GetClass()->flags & INSTANCE_HIDDEN))
return createIndex(row, column, parentItem->GetChildren()[row].get());
return {};
}

View file

@ -33,7 +33,7 @@ void PlaceDocument::setRunState(RunState newState) {
std::shared_ptr<DataModel> newModel = editModeDataModel->CloneModel();
gDataModel = newModel;
gDataModel->Init();
gDataModel->Init(true);
} else if (newState == RUN_PAUSED && _runState == RUN_RUNNING) {
_runState = RUN_PAUSED;
} else if (newState == RUN_STOPPED) {
@ -41,7 +41,6 @@ void PlaceDocument::setRunState(RunState newState) {
// GC: Check to make sure gDataModel gets properly garbage collected prior to this
gDataModel = editModeDataModel;
gDataModel->Init();
}
}