mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-04-08 21:50:08 +00:00
- LuaWrapper creates a simple event layer for lua states. Allowing events to have registered callbacks & be waited on until fired - EventWrapper exposes this api to scripts
158 lines
4.5 KiB
C++
158 lines
4.5 KiB
C++
#include "lua/LuaManager.hpp"
|
|
#include "lua/LuaWrapper.hpp"
|
|
#include "lua/EventWrapper.hpp"
|
|
|
|
#include "servers/CNShardServer.hpp"
|
|
#include "settings.hpp"
|
|
|
|
#include <filesystem>
|
|
#include <vector>
|
|
|
|
time_t getTime();
|
|
class Script;
|
|
|
|
// our "main" state, holds our environment
|
|
lua_State *LuaManager::global;
|
|
std::map<lua_State*, Script*> activeScripts;
|
|
|
|
/*
|
|
Basically each script is treated as a coroutine, when wait() is called it gets yielded and is pushed onto the scheduler queue to be resumed.
|
|
*/
|
|
class Script {
|
|
private:
|
|
lua_State *thread;
|
|
lRegistry threadRef; // we'll need to unref this when closing this state
|
|
|
|
public:
|
|
Script(std::string source) {
|
|
// make the thread & register it in the registry
|
|
thread = lua_newthread(LuaManager::global);
|
|
threadRef = luaL_ref(LuaManager::global, LUA_REGISTRYINDEX);
|
|
|
|
// add this script to the map
|
|
activeScripts[thread] = this;
|
|
|
|
// compile & run the script, if it error'd, print the error
|
|
int _retCode;
|
|
if (luaL_loadfile(thread, source.c_str()) || ((_retCode = lua_resume(thread, 0)) != 0 && (_retCode != LUA_YIELD))) {
|
|
std::cout << "[LUA ERROR]: " << lua_tostring(thread, -1) << std::endl;
|
|
}
|
|
}
|
|
|
|
// unregister all of our events from the wrappers
|
|
~Script() {
|
|
LuaManager::clearState(thread);
|
|
|
|
// remove it from the global registry
|
|
luaL_unref(LuaManager::global, LUA_REGISTRYINDEX, threadRef);
|
|
}
|
|
|
|
// c++ moment....
|
|
lua_State* getState() {
|
|
return thread;
|
|
}
|
|
};
|
|
|
|
struct scheduledThread {
|
|
lRegistry ref; // ref that should be unref'd before resuming
|
|
time_t time;
|
|
};
|
|
std::map<lua_State*, scheduledThread> scheduleQueue;
|
|
|
|
// pauses the script for x seconds
|
|
int OF_wait(lua_State *state) {
|
|
double seconds = luaL_checknumber(state, 1);
|
|
|
|
// register the thread in the global registry so we don't get GC'd
|
|
lua_pushthread(state);
|
|
lRegistry ref = luaL_ref(state, LUA_REGISTRYINDEX); // threads decentant of a global state all share the global registry
|
|
|
|
// yield the state and push the state onto our scheduler queue
|
|
scheduleQueue[state] = {ref, (int)(seconds*1000) + getTime()};
|
|
return lua_yield(state, 0);
|
|
}
|
|
|
|
void luaScheduler(CNServer *serv, time_t currtime) {
|
|
for (auto iter = scheduleQueue.begin(); iter != scheduleQueue.end();) {
|
|
time_t event = (*iter).second.time;
|
|
lRegistry ref = (*iter).second.ref;
|
|
lua_State *thread = (*iter).first;
|
|
// is it time to run the event?
|
|
if (event <= currtime) {
|
|
// remove from the scheduler queue
|
|
scheduleQueue.erase(iter++);
|
|
|
|
// unregister the thread
|
|
luaL_unref(thread, LUA_REGISTRYINDEX, ref);
|
|
|
|
// resume the state, (wait() returns the delta time since call)
|
|
lua_pushnumber(thread, ((double)currtime - event)/10);
|
|
yieldCall(thread, 1);
|
|
} else // go to the next iteration
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
void LuaManager::printError(std::string err) {
|
|
std::cerr << "[LUA ERR]: " << err << std::endl;
|
|
}
|
|
|
|
void LuaManager::init() {
|
|
// allocate our state
|
|
global = luaL_newstate();
|
|
|
|
// open lua's base libraries (excluding the IO for now)
|
|
luaopen_base(global);
|
|
luaopen_table(global);
|
|
luaopen_string(global);
|
|
luaopen_math(global);
|
|
luaopen_debug(global);
|
|
|
|
// add wait()
|
|
lua_register(global, "wait", OF_wait);
|
|
|
|
// register our libraries
|
|
Event::init(global);
|
|
|
|
activeScripts = std::map<lua_State*, Script*>();
|
|
|
|
REGISTER_SHARD_TIMER(luaScheduler, 200);
|
|
|
|
// load our scripts
|
|
loadScripts();
|
|
}
|
|
|
|
void LuaManager::runScript(std::string filename) {
|
|
new Script(filename);
|
|
}
|
|
|
|
void LuaManager::stopScripts() {
|
|
// clear the scheduler queue
|
|
scheduleQueue.clear();
|
|
|
|
// free all the scripts, they'll take care of everything for us :)
|
|
for (auto as : activeScripts) {
|
|
delete as.second;
|
|
}
|
|
|
|
// finally clear the map
|
|
activeScripts.clear();
|
|
}
|
|
|
|
void LuaManager::loadScripts() {
|
|
if (!std::filesystem::exists(settings::SCRIPTSDIR)) {
|
|
std::cout << "[WARN] scripts directory \"" << settings::SCRIPTSDIR << "\" doesn't exist!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
// for each file in the scripts director, load the script
|
|
std::filesystem::path dir(settings::SCRIPTSDIR);
|
|
for (auto &d : std::filesystem::directory_iterator(dir)) {
|
|
if (d.path().extension().u8string() == ".lua")
|
|
runScript(d.path().u8string());
|
|
}
|
|
}
|
|
|
|
void LuaManager::clearState(lua_State *state) {
|
|
// TODO
|
|
} |