Compare commits
4 commits
621ae30464
...
626be7107f
Author | SHA1 | Date | |
---|---|---|---|
626be7107f | |||
13cad8e01a | |||
26459c9275 | |||
a1bd6e1bbc |
12 changed files with 213 additions and 21 deletions
|
@ -1,4 +1,5 @@
|
||||||
#include "script.h"
|
#include "script.h"
|
||||||
|
#include "common.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
|
@ -7,38 +8,86 @@
|
||||||
#include "objects/datamodel.h"
|
#include "objects/datamodel.h"
|
||||||
#include "datatypes/ref.h"
|
#include "datatypes/ref.h"
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
int script_wait(lua_State*);
|
||||||
|
int script_delay(lua_State*);
|
||||||
|
|
||||||
Script::Script(): Instance(&TYPE) {
|
Script::Script(): Instance(&TYPE) {
|
||||||
source = "print \"Hello, world!\"";
|
source = "print \"Hello, world!\"\nwait(1)print \"Wait success! :D\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
Script::~Script() {
|
Script::~Script() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::Run() {
|
void Script::Run() {
|
||||||
lua_State* L = dataModel().value()->GetService<ScriptContext>()->state;
|
std::shared_ptr<ScriptContext> scriptContext = dataModel().value()->GetService<ScriptContext>();
|
||||||
|
|
||||||
|
lua_State* L = scriptContext->state;
|
||||||
|
|
||||||
|
// Create thread
|
||||||
|
this->thread = lua_newthread(L);
|
||||||
|
lua_State* Lt = thread;
|
||||||
|
|
||||||
// Initialize script globals
|
// Initialize script globals
|
||||||
lua_getglobal(L, "_G");
|
lua_getglobal(Lt, "_G");
|
||||||
|
|
||||||
lua_pushstring(L, "game");
|
Data::InstanceRef(dataModel().value()).PushLuaValue(Lt);
|
||||||
Data::InstanceRef(dataModel().value()).PushLuaValue(L);
|
lua_setfield(Lt, -2, "game");
|
||||||
lua_rawset(L, -3);
|
|
||||||
|
|
||||||
lua_pushstring(L, "workspace");
|
Data::InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
|
||||||
Data::InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(L);
|
lua_setfield(Lt, -2, "workspace");
|
||||||
lua_rawset(L, -3);
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
lua_pushlightuserdata(Lt, scriptContext.get());
|
||||||
|
lua_pushcclosure(Lt, script_wait, 1);
|
||||||
|
lua_setfield(Lt, -2, "wait");
|
||||||
|
|
||||||
luaL_loadstring(L, source.c_str());
|
lua_pushlightuserdata(Lt, scriptContext.get());
|
||||||
int status = lua_pcall(L, 0, LUA_MULTRET, 0);
|
lua_pushcclosure(Lt, script_delay, 1);
|
||||||
if (status != 0) {
|
lua_setfield(Lt, -2, "delay");
|
||||||
Logger::error(lua_tostring(L, -1));
|
|
||||||
lua_pop(L, 1);
|
lua_pop(Lt, 1); // _G
|
||||||
|
|
||||||
|
// Load source and push onto thread stack as function ptr
|
||||||
|
luaL_loadstring(Lt, source.c_str());
|
||||||
|
|
||||||
|
int status = lua_resume(Lt, 0);
|
||||||
|
if (status > LUA_YIELD) {
|
||||||
|
Logger::error(lua_tostring(Lt, -1));
|
||||||
|
lua_pop(Lt, 1); // Pop return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1); // Pop the thread
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::Stop() {
|
void Script::Stop() {
|
||||||
// TODO:
|
// TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
int script_wait(lua_State* L) {
|
||||||
|
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
|
||||||
|
|
||||||
|
scriptContext->PushThreadSleep(L, secs);
|
||||||
|
lua_pop(L, 1); // pop secs
|
||||||
|
|
||||||
|
// Yield
|
||||||
|
return lua_yield(L, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int script_delay(lua_State* L) {
|
||||||
|
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
float secs = std::max(luaL_checknumber(L, 1), 0.03);
|
||||||
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||||
|
|
||||||
|
lua_State* Lt = lua_newthread(L); // Create a new thread
|
||||||
|
lua_pop(L, 1); // pop the newly created thread so that xmove moves func instead of it into itself
|
||||||
|
lua_xmove(L, Lt, 1); // move func
|
||||||
|
lua_pop(L, 1); // pop secs
|
||||||
|
|
||||||
|
// Schedule next run
|
||||||
|
scriptContext->PushThreadSleep(Lt, secs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
class DEF_INST_(explorer_icon="script") Script : public Instance {
|
class DEF_INST_(explorer_icon="script") Script : public Instance {
|
||||||
AUTOGEN_PREAMBLE
|
AUTOGEN_PREAMBLE
|
||||||
|
|
||||||
|
lua_State* thread;
|
||||||
public:
|
public:
|
||||||
Script();
|
Script();
|
||||||
~Script();
|
~Script();
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
#include "scriptcontext.h"
|
#include "scriptcontext.h"
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
#include "datatypes/meta.h"
|
#include "datatypes/color3.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "timeutil.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <luajit-2.1/lauxlib.h>
|
#include <ctime>
|
||||||
#include <luajit-2.1/lua.h>
|
#include <chrono>
|
||||||
#include <luajit-2.1/lualib.h>
|
#include "lua.h"
|
||||||
|
|
||||||
static int g_print(lua_State*);
|
static int g_print(lua_State*);
|
||||||
static int g_require(lua_State*);
|
static int g_require(lua_State*);
|
||||||
|
@ -62,8 +63,71 @@ void ScriptContext::InitService() {
|
||||||
lua_rawset(state, -3);
|
lua_rawset(state, -3);
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pop(state, 1);
|
lua_pop(state, 1); // _G
|
||||||
|
|
||||||
|
lua_pushlightuserdata(state, &sleepingThreads);
|
||||||
|
lua_newtable(state);
|
||||||
|
lua_settable(state, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptContext::PushThreadSleep(lua_State* thread, float delay) {
|
||||||
|
// A thread is allowed to sleep multiple times at once, though this is a very edge-case scenario
|
||||||
|
|
||||||
|
SleepingThread sleep;
|
||||||
|
sleep.thread = thread;
|
||||||
|
sleep.timeYieldedWhen = tu_clock_micros();
|
||||||
|
sleep.targetTimeMicros = tu_clock_micros() + delay * 1'000'000;
|
||||||
|
|
||||||
|
sleepingThreads.push_back(sleep);
|
||||||
|
|
||||||
|
// Add to registry so it doesn't get GC'd
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/17138663/16255372
|
||||||
|
lua_pushlightuserdata(state, &sleepingThreads);
|
||||||
|
lua_gettable(state, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
lua_pushthread(thread); // key
|
||||||
|
lua_xmove(thread, state, 1);
|
||||||
|
lua_pushboolean(state, true); // value
|
||||||
|
lua_rawset(state, -3); // set
|
||||||
|
|
||||||
|
lua_pop(state, 1); // pop sleepingThreads
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptContext::RunSleepingThreads() {
|
||||||
|
for (int i = 0; i < sleepingThreads.size();) {
|
||||||
|
bool deleted = false;
|
||||||
|
|
||||||
|
SleepingThread sleep = sleepingThreads[i];
|
||||||
|
if (tu_clock_micros() >= sleep.targetTimeMicros) {
|
||||||
|
// Time args
|
||||||
|
lua_pushnumber(sleep.thread, float(tu_clock_micros() - sleep.timeYieldedWhen) / 1'000'000);
|
||||||
|
lua_pushnumber(sleep.thread, float(tu_clock_micros()) / 1'000'000);
|
||||||
|
int status = lua_resume(sleep.thread, 2);
|
||||||
|
if (status > LUA_YIELD) {
|
||||||
|
Logger::error(lua_tostring(sleep.thread, -1));
|
||||||
|
lua_pop(sleep.thread, 1); // Pop return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove thread
|
||||||
|
deleted = true;
|
||||||
|
sleepingThreads.erase(sleepingThreads.begin() + i);
|
||||||
|
|
||||||
|
// Erase from registry
|
||||||
|
lua_pushlightuserdata(state, &sleepingThreads);
|
||||||
|
lua_gettable(state, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
lua_pushthread(sleep.thread); // key
|
||||||
|
lua_xmove(sleep.thread, state, 1);
|
||||||
|
lua_pushnil(state);
|
||||||
|
lua_rawset(state, -3); // set
|
||||||
|
|
||||||
|
lua_pop(state, 1); // sleepingThreads
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deleted)
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.lua.org/source/5.1/lbaselib.c.html
|
// https://www.lua.org/source/5.1/lbaselib.c.html
|
||||||
|
|
|
@ -3,9 +3,19 @@
|
||||||
#include "objects/annotation.h"
|
#include "objects/annotation.h"
|
||||||
#include "objects/base/service.h"
|
#include "objects/base/service.h"
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct SleepingThread {
|
||||||
|
lua_State* thread;
|
||||||
|
uint64_t timeYieldedWhen;
|
||||||
|
uint64_t targetTimeMicros;
|
||||||
|
bool active = true;
|
||||||
|
};
|
||||||
|
|
||||||
class DEF_INST_SERVICE ScriptContext : public Service {
|
class DEF_INST_SERVICE ScriptContext : public Service {
|
||||||
AUTOGEN_PREAMBLE
|
AUTOGEN_PREAMBLE
|
||||||
|
|
||||||
|
std::vector<SleepingThread> sleepingThreads;
|
||||||
protected:
|
protected:
|
||||||
void InitService() override;
|
void InitService() override;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
@ -15,6 +25,8 @@ public:
|
||||||
~ScriptContext();
|
~ScriptContext();
|
||||||
|
|
||||||
lua_State* state;
|
lua_State* state;
|
||||||
|
void PushThreadSleep(lua_State* thread, float delay);
|
||||||
|
void RunSleepingThreads();
|
||||||
|
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ScriptContext>(); };
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ScriptContext>(); };
|
||||||
};
|
};
|
|
@ -111,7 +111,7 @@ void Workspace::PhysicsStep(float deltaTime) {
|
||||||
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
|
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
|
||||||
|
|
||||||
std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect();
|
std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect();
|
||||||
float rate = motor->part0.lock()->GetSurfaceParamB(motor->c0.LookVector().Unit()) * 30;
|
float rate = motor->part0.lock()->GetSurfaceParamB(-motor->c0.LookVector().Unit()) * 30;
|
||||||
// part->rigidBody->enableGravity(false);
|
// part->rigidBody->enableGravity(false);
|
||||||
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
|
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
|
||||||
}
|
}
|
||||||
|
|
12
core/src/timeutil.cpp
Normal file
12
core/src/timeutil.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "timeutil.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
tu_time_t TIME_STARTED_MICROS = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch().count();
|
||||||
|
|
||||||
|
|
||||||
|
tu_time_t tu_clock_micros() {
|
||||||
|
tu_time_t now = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch().count();;
|
||||||
|
|
||||||
|
return now - TIME_STARTED_MICROS;
|
||||||
|
}
|
8
core/src/timeutil.h
Normal file
8
core/src/timeutil.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
typedef uint64_t tu_time_t;
|
||||||
|
|
||||||
|
// Provides a high-accuracy time since the program started in microseconds (via std::chrono)
|
||||||
|
tu_time_t tu_clock_micros();
|
|
@ -193,6 +193,8 @@ void MainWindow::connectActionHandlers() {
|
||||||
connect(ui->actionToolStuds, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_STUDS : TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolStuds, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_STUDS : TOOL_SELECT; updateToolbars(); });
|
||||||
connect(ui->actionToolInlets, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_INLETS : TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolInlets, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_INLETS : TOOL_SELECT; updateToolbars(); });
|
||||||
connect(ui->actionToolUniversal, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_UNIVERSAL : TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolUniversal, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_UNIVERSAL : TOOL_SELECT; updateToolbars(); });
|
||||||
|
connect(ui->actionToolHinge, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_HINGE : TOOL_SELECT; updateToolbars(); });
|
||||||
|
connect(ui->actionToolMotor, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_MOTOR : TOOL_SELECT; updateToolbars(); });
|
||||||
ui->actionToolSelect->setChecked(true);
|
ui->actionToolSelect->setChecked(true);
|
||||||
selectedTool = TOOL_SELECT;
|
selectedTool = TOOL_SELECT;
|
||||||
|
|
||||||
|
@ -447,6 +449,8 @@ void MainWindow::updateToolbars() {
|
||||||
ui->actionToolStuds->setChecked(selectedTool == TOOL_STUDS);
|
ui->actionToolStuds->setChecked(selectedTool == TOOL_STUDS);
|
||||||
ui->actionToolInlets->setChecked(selectedTool == TOOL_INLETS);
|
ui->actionToolInlets->setChecked(selectedTool == TOOL_INLETS);
|
||||||
ui->actionToolUniversal->setChecked(selectedTool == TOOL_UNIVERSAL);
|
ui->actionToolUniversal->setChecked(selectedTool == TOOL_UNIVERSAL);
|
||||||
|
ui->actionToolHinge->setChecked(selectedTool == TOOL_HINGE);
|
||||||
|
ui->actionToolMotor->setChecked(selectedTool == TOOL_MOTOR);
|
||||||
|
|
||||||
ui->actionGridSnap1->setChecked(snappingMode == GridSnappingMode::SNAP_1_STUD);
|
ui->actionGridSnap1->setChecked(snappingMode == GridSnappingMode::SNAP_1_STUD);
|
||||||
ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS);
|
ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS);
|
||||||
|
|
|
@ -22,6 +22,8 @@ enum SelectedTool {
|
||||||
TOOL_STUDS,
|
TOOL_STUDS,
|
||||||
TOOL_INLETS,
|
TOOL_INLETS,
|
||||||
TOOL_UNIVERSAL,
|
TOOL_UNIVERSAL,
|
||||||
|
TOOL_HINGE,
|
||||||
|
TOOL_MOTOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum GridSnappingMode {
|
enum GridSnappingMode {
|
||||||
|
|
|
@ -221,6 +221,8 @@
|
||||||
<addaction name="actionToolStuds"/>
|
<addaction name="actionToolStuds"/>
|
||||||
<addaction name="actionToolInlets"/>
|
<addaction name="actionToolInlets"/>
|
||||||
<addaction name="actionToolUniversal"/>
|
<addaction name="actionToolUniversal"/>
|
||||||
|
<addaction name="actionToolHinge"/>
|
||||||
|
<addaction name="actionToolMotor"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QToolBar" name="toolBar">
|
<widget class="QToolBar" name="toolBar">
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -673,6 +675,40 @@
|
||||||
<enum>QAction::MenuRole::NoRole</enum>
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionToolHinge">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="surface-weld"/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hinge</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Hinge</string>
|
||||||
|
</property>
|
||||||
|
<property name="menuRole">
|
||||||
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionToolMotor">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="surface-weld"/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Motor</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Motor</string>
|
||||||
|
</property>
|
||||||
|
<property name="menuRole">
|
||||||
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionToggleEditSounds">
|
<action name="actionToggleEditSounds">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "objects/joint/snap.h"
|
#include "objects/joint/snap.h"
|
||||||
#include "objects/script.h"
|
#include "objects/script.h"
|
||||||
|
#include "objects/script/scriptcontext.h"
|
||||||
#include "rendering/surface.h"
|
#include "rendering/surface.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -67,6 +68,7 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) {
|
||||||
gWorkspace()->PhysicsStep(deltaTime);
|
gWorkspace()->PhysicsStep(deltaTime);
|
||||||
placeWidget->repaint();
|
placeWidget->repaint();
|
||||||
placeWidget->updateCycle();
|
placeWidget->updateCycle();
|
||||||
|
gDataModel->GetService<ScriptContext>()->RunSleepingThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ ScriptDocument::ScriptDocument(std::shared_ptr<Script> script, QWidget* parent):
|
||||||
scintilla->setMarginsBackgroundColor(palette().window().color());
|
scintilla->setMarginsBackgroundColor(palette().window().color());
|
||||||
scintilla->setCaretForegroundColor(palette().text().color());
|
scintilla->setCaretForegroundColor(palette().text().color());
|
||||||
scintilla->setFont(font);
|
scintilla->setFont(font);
|
||||||
|
scintilla->setTabWidth(4);
|
||||||
|
|
||||||
scintilla->setText(QString::fromStdString(script->source));
|
scintilla->setText(QString::fromStdString(script->source));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue