Lua: Add variadic print, LuaAPI

This commit is contained in:
Lion Kortlepel
2021-09-16 11:54:52 +02:00
parent 5978665ad6
commit 9b9c18a4c1
8 changed files with 213 additions and 40 deletions

View File

@@ -2,12 +2,11 @@
#include "CustomAssert.h"
#include "TLuaPlugin.h"
#include "LuaAPI.h"
#include <chrono>
#include <random>
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
static std::mt19937_64 MTGen64;
static TLuaStateId GenerateUniqueStateId() {
@@ -18,6 +17,7 @@ static TLuaStateId GenerateUniqueStateId() {
TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
: mNetwork(Network)
, mServer(Server) {
LuaAPI::MP::Engine = this;
if (!fs::exists(Application::Settings.Resource)) {
fs::create_directory(Application::Settings.Resource);
}
@@ -38,7 +38,16 @@ TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
void TLuaEngine::operator()() {
RegisterThread("LuaEngine");
// lua engine main thread
CollectPlugins();
CollectAndInitPlugins();
// now call all onInit's
for (const auto& Pair : mLuaStates) {
auto Res = EnqueueFunctionCall(Pair.first, "onInit");
Res->WaitUntilReady();
if (Res->Error && Res->ErrorMessage != TLuaEngine::BeamMPFnNotFoundError) {
beammp_lua_error("Calling \"onInit\" on \"" + Pair.first + "\" failed: " + Res->ErrorMessage);
}
}
// this thread handles timers
while (!mShutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
@@ -46,12 +55,17 @@ void TLuaEngine::operator()() {
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueScript(TLuaStateId StateID, const std::shared_ptr<std::string>& Script) {
std::unique_lock Lock(mLuaStatesMutex);
TLuaResult Result;
beammp_debug("enqueuing script into \"" + StateID + "\"");
return mLuaStates.at(StateID)->EnqueueScript(Script);
}
void TLuaEngine::CollectPlugins() {
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName) {
std::unique_lock Lock(mLuaStatesMutex);
beammp_debug("calling \"" + FunctionName + "\" in \"" + StateID + "\"");
return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName);
}
void TLuaEngine::CollectAndInitPlugins() {
for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) {
auto Path = Dir.path();
Path = fs::relative(Path);
@@ -69,9 +83,10 @@ void TLuaEngine::CollectPlugins() {
void TLuaEngine::InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config) {
beammp_assert(fs::exists(Folder));
beammp_assert(fs::is_directory(Folder));
TLuaPlugin Plugin(*this, Config);
std::unique_lock Lock(mLuaStatesMutex);
EnsureStateExists(Config.StateId, Folder.stem().string());
EnsureStateExists(Config.StateId, Folder.stem().string(), true);
Lock.unlock();
TLuaPlugin Plugin(*this, Config, Folder);
}
void TLuaEngine::FindAndParseConfig(const fs::path& Folder, TLuaPluginConfig& Config) {
@@ -96,56 +111,113 @@ void TLuaEngine::FindAndParseConfig(const fs::path& Folder, TLuaPluginConfig& Co
}
}
void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name) {
void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit) {
if (mLuaStates.find(StateId) == mLuaStates.end()) {
beammp_debug("Creating lua state for state id \"" + StateId + "\"");
auto DataPtr = std::make_unique<StateThreadData>(Name, mShutdown);
auto DataPtr = std::make_unique<StateThreadData>(Name, mShutdown, StateId);
mLuaStates[StateId] = std::move(DataPtr);
if (!DontCallOnInit) {
auto Res = EnqueueFunctionCall(StateId, "onInit");
Res->WaitUntilReady();
if (Res->Error && Res->ErrorMessage != TLuaEngine::BeamMPFnNotFoundError) {
beammp_lua_error("Calling \"onInit\" on \"" + StateId + "\" failed: " + Res->ErrorMessage);
}
}
}
}
TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown)
TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId)
: mName(Name)
, mShutdown(Shutdown) {
, mShutdown(Shutdown)
, mStateId(StateId) {
mState = luaL_newstate();
luaL_openlibs(mState);
sol::state_view StateView(mState);
auto LuaPrint = [](const std::string& Msg) { luaprint(Msg); };
StateView.set_function("print", LuaPrint);
StateView.set_function("print", &LuaAPI::Print);
auto Table = StateView.create_named_table("MP");
Table.set_function("GetOSName", &LuaAPI::MP::GetOSName);
Table.set_function("GetServerVersion", &LuaAPI::MP::GetServerVersion);
Start();
}
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const std::shared_ptr<std::string>& Script) {
beammp_debug("enqueuing script into \"" + mName + "\"");
beammp_debug("enqueuing script into \"" + mStateId + "\"");
std::unique_lock Lock(mStateExecuteQueueMutex);
auto Result = std::make_shared<TLuaResult>();
mStateExecuteQueue.push({ Script, Result });
return Result;
}
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName) {
beammp_debug("calling \"" + FunctionName + "\" in \"" + mName + "\"");
std::unique_lock Lock(mStateFunctionQueueMutex);
auto Result = std::make_shared<TLuaResult>();
mStateFunctionQueue.push({ FunctionName, Result });
return Result;
}
void TLuaEngine::StateThreadData::operator()() {
RegisterThread(mName);
RegisterThread("Lua:" + mStateId);
while (!mShutdown) {
std::unique_lock Lock(mStateExecuteQueueMutex);
if (mStateExecuteQueue.empty()) {
Lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else {
auto S = mStateExecuteQueue.front();
mStateExecuteQueue.pop();
Lock.unlock();
beammp_debug("Running script");
sol::state_view StateView(mState);
auto Res = StateView.safe_script(*S.first, sol::script_pass_on_error);
if (Res.valid()) {
S.second->Error = false;
{ // StateExecuteQueue Scope
std::unique_lock Lock(mStateExecuteQueueMutex);
if (mStateExecuteQueue.empty()) {
Lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else {
S.second->Error = true;
sol::error Err = Res;
S.second->ErrorMessage = Err.what();
auto S = mStateExecuteQueue.front();
mStateExecuteQueue.pop();
Lock.unlock();
beammp_debug("Running script");
sol::state_view StateView(mState);
auto Res = StateView.safe_script(*S.first, sol::script_pass_on_error);
S.second->Ready = true;
if (Res.valid()) {
S.second->Error = false;
S.second->Result = std::move(Res);
} else {
S.second->Error = true;
sol::error Err = Res;
S.second->ErrorMessage = Err.what();
}
}
S.second->Ready = true;
}
{ // StateFunctionQueue Scope
std::unique_lock Lock(mStateFunctionQueueMutex);
if (mStateFunctionQueue.empty()) {
Lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else {
auto FnNameResultPair = mStateFunctionQueue.front();
mStateFunctionQueue.pop();
Lock.unlock();
beammp_debug("Running function");
sol::state_view StateView(mState);
auto Fn = StateView[FnNameResultPair.first];
if (Fn.valid() && Fn.get_type() == sol::type::function) {
auto Res = Fn();
FnNameResultPair.second->Ready = true;
if (Res.valid()) {
FnNameResultPair.second->Error = false;
FnNameResultPair.second->Result = std::move(Res);
} else {
FnNameResultPair.second->Error = true;
sol::error Err = Res;
FnNameResultPair.second->ErrorMessage = Err.what();
}
} else {
FnNameResultPair.second->Ready = true;
FnNameResultPair.second->Error = true;
FnNameResultPair.second->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later
}
}
}
}
}
void TLuaResult::WaitUntilReady() {
while (!Ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}