mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-15 11:13:45 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c740ccedf | |||
| b0cf5c7838 | |||
| 586510041d | |||
| 1794c3fe45 | |||
| ee599e970d | |||
| 40158dc252 | |||
| 5ece60574a | |||
| c605498a2d | |||
| 4d7967d5d9 | |||
| d81087b5af | |||
| 7e1f86478d | |||
| c85e026c2d | |||
| e4826e8bf1 | |||
| 590b159f14 | |||
| 40533c04bc | |||
| 946c1362e1 | |||
| 4347cb4af2 | |||
| 88721d4f7f | |||
| 7deea900fb | |||
| cbc1483537 | |||
| 39badf432d | |||
| 03307e71fb |
@@ -49,6 +49,7 @@ set(PRJ_HEADERS
|
|||||||
include/TServer.h
|
include/TServer.h
|
||||||
include/VehicleData.h
|
include/VehicleData.h
|
||||||
include/Env.h
|
include/Env.h
|
||||||
|
include/Profiling.h
|
||||||
)
|
)
|
||||||
# add all source files (.cpp) to this, except the one with main()
|
# add all source files (.cpp) to this, except the one with main()
|
||||||
set(PRJ_SOURCES
|
set(PRJ_SOURCES
|
||||||
@@ -72,6 +73,7 @@ set(PRJ_SOURCES
|
|||||||
src/TServer.cpp
|
src/TServer.cpp
|
||||||
src/VehicleData.cpp
|
src/VehicleData.cpp
|
||||||
src/Env.cpp
|
src/Env.cpp
|
||||||
|
src/Profiling.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Lua REQUIRED)
|
find_package(Lua REQUIRED)
|
||||||
|
|||||||
+1
-1
@@ -150,7 +150,7 @@ private:
|
|||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
static inline Version mVersion { 3, 3, 0 };
|
static inline Version mVersion { 3, 4, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/thread/synchronized_value.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <limits>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace prof {
|
||||||
|
|
||||||
|
using Duration = std::chrono::duration<double, std::milli>;
|
||||||
|
using TimePoint = std::chrono::high_resolution_clock::time_point;
|
||||||
|
|
||||||
|
/// Returns the current time.
|
||||||
|
TimePoint now();
|
||||||
|
|
||||||
|
/// Returns a sub-millisecond resolution duration between start and end.
|
||||||
|
Duration duration(const TimePoint& start, const TimePoint& end);
|
||||||
|
|
||||||
|
struct Stats {
|
||||||
|
double mean;
|
||||||
|
double stdev;
|
||||||
|
double min;
|
||||||
|
double max;
|
||||||
|
size_t n;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Calculates and stores the moving average over K samples of execution time data
|
||||||
|
/// for some single unit of code. Threadsafe.
|
||||||
|
struct UnitExecutionTime {
|
||||||
|
UnitExecutionTime();
|
||||||
|
|
||||||
|
/// Adds a sample to the collection, overriding the oldest sample if needed.
|
||||||
|
void add_sample(const Duration& dur);
|
||||||
|
|
||||||
|
/// Calculates the mean duration over the `measurement_count()` measurements,
|
||||||
|
/// as well as the standard deviation.
|
||||||
|
Stats stats() const;
|
||||||
|
|
||||||
|
/// Returns the number of elements the moving average is calculated over.
|
||||||
|
size_t measurement_count() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex m_mtx {};
|
||||||
|
size_t m_total_calls {};
|
||||||
|
double m_sum {};
|
||||||
|
// sum of measurements squared (for running stdev)
|
||||||
|
double m_measurement_sqr_sum {};
|
||||||
|
double m_min { std::numeric_limits<double>::max() };
|
||||||
|
double m_max { std::numeric_limits<double>::min() };
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds profiles for multiple units by name. Threadsafe.
|
||||||
|
struct UnitProfileCollection {
|
||||||
|
/// Adds a sample to the collection, overriding the oldest sample if needed.
|
||||||
|
void add_sample(const std::string& unit, const Duration& duration);
|
||||||
|
|
||||||
|
/// Calculates the mean duration over the `measurement_count()` measurements,
|
||||||
|
/// as well as the standard deviation.
|
||||||
|
Stats stats(const std::string& unit);
|
||||||
|
|
||||||
|
/// Returns the number of elements the moving average is calculated over.
|
||||||
|
size_t measurement_count(const std::string& unit);
|
||||||
|
|
||||||
|
/// Returns the stats for all stored units.
|
||||||
|
std::unordered_map<std::string, Stats> all_stats();
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::synchronized_value<std::unordered_map<std::string, UnitExecutionTime>> m_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
+34
-12
@@ -18,9 +18,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Profiling.h"
|
||||||
#include "TNetwork.h"
|
#include "TNetwork.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
#include <any>
|
#include <any>
|
||||||
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
@@ -28,6 +30,7 @@
|
|||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <set>
|
#include <set>
|
||||||
@@ -36,19 +39,34 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define SOL_ALL_SAFETIES_ON 1
|
#define SOL_ALL_SAFETIES_ON 1
|
||||||
|
#define SOL_USER_C_ASSERT SOL_ON
|
||||||
|
#define SOL_C_ASSERT(...) \
|
||||||
|
beammp_lua_errorf("SOL2 assertion failure: Assertion `{}` failed in {}:{}. This *should* be a fatal error, but BeamMP Server overrides it to not be fatal. This may cause the Lua Engine to crash, or cause other issues.", #__VA_ARGS__, __FILE__, __LINE__)
|
||||||
#include <sol/sol.hpp>
|
#include <sol/sol.hpp>
|
||||||
|
|
||||||
|
struct JsonString {
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// value used to keep nils in a table or array, across serialization boundaries like
|
||||||
|
// JsonEncode, so that the nil stays at the same index and isn't treated like a special
|
||||||
|
// value (e.g. one that can be ignored or discarded).
|
||||||
|
const inline std::string BEAMMP_INTERNAL_NIL = "BEAMMP_SERVER_INTERNAL_NIL_VALUE";
|
||||||
|
|
||||||
using TLuaStateId = std::string;
|
using TLuaStateId = std::string;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
/**
|
/**
|
||||||
* std::variant means, that TLuaArgTypes may be one of the Types listed as template args
|
* std::variant means, that TLuaArgTypes may be one of the Types listed as template args
|
||||||
*/
|
*/
|
||||||
using TLuaArgTypes = std::variant<std::string, int, sol::variadic_args, bool, std::unordered_map<std::string, std::string>>;
|
using TLuaValue = std::variant<std::string, int, JsonString, bool, std::unordered_map<std::string, std::string>, float>;
|
||||||
static constexpr size_t TLuaArgTypes_String = 0;
|
enum TLuaType {
|
||||||
static constexpr size_t TLuaArgTypes_Int = 1;
|
String = 0,
|
||||||
static constexpr size_t TLuaArgTypes_VariadicArgs = 2;
|
Int = 1,
|
||||||
static constexpr size_t TLuaArgTypes_Bool = 3;
|
Json = 2,
|
||||||
static constexpr size_t TLuaArgTypes_StringStringMap = 4;
|
Bool = 3,
|
||||||
|
StringStringMap = 4,
|
||||||
|
Float = 5,
|
||||||
|
};
|
||||||
|
|
||||||
class TLuaPlugin;
|
class TLuaPlugin;
|
||||||
|
|
||||||
@@ -96,7 +114,7 @@ public:
|
|||||||
struct QueuedFunction {
|
struct QueuedFunction {
|
||||||
std::string FunctionName;
|
std::string FunctionName;
|
||||||
std::shared_ptr<TLuaResult> Result;
|
std::shared_ptr<TLuaResult> Result;
|
||||||
std::vector<TLuaArgTypes> Args;
|
std::vector<TLuaValue> Args;
|
||||||
std::string EventName; // optional, may be empty
|
std::string EventName; // optional, may be empty
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,7 +167,7 @@ public:
|
|||||||
void ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results);
|
void ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results);
|
||||||
bool HasState(TLuaStateId StateId);
|
bool HasState(TLuaStateId StateId);
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script);
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaValue>& Args);
|
||||||
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
|
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
|
||||||
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
|
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
|
||||||
/**
|
/**
|
||||||
@@ -169,7 +187,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<TLuaResult>> Results;
|
std::vector<std::shared_ptr<TLuaResult>> Results;
|
||||||
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... };
|
std::vector<TLuaValue> Arguments { TLuaValue { std::forward<ArgsT>(Args) }... };
|
||||||
|
|
||||||
for (const auto& Event : mLuaEvents.at(EventName)) {
|
for (const auto& Event : mLuaEvents.at(EventName)) {
|
||||||
for (const auto& Function : Event.second) {
|
for (const auto& Function : Event.second) {
|
||||||
@@ -188,7 +206,7 @@ public:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
std::vector<std::shared_ptr<TLuaResult>> Results;
|
std::vector<std::shared_ptr<TLuaResult>> Results;
|
||||||
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... };
|
std::vector<TLuaValue> Arguments { TLuaValue { std::forward<ArgsT>(Args) }... };
|
||||||
const auto Handlers = GetEventHandlersForState(EventName, StateId);
|
const auto Handlers = GetEventHandlersForState(EventName, StateId);
|
||||||
for (const auto& Handler : Handlers) {
|
for (const auto& Handler : Handlers) {
|
||||||
Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments));
|
Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments));
|
||||||
@@ -225,8 +243,8 @@ private:
|
|||||||
StateThreadData(const StateThreadData&) = delete;
|
StateThreadData(const StateThreadData&) = delete;
|
||||||
virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaValue>& Args);
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaValue>& Args, const std::string& EventName, CallStrategy Strategy);
|
||||||
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
||||||
void AddPath(const fs::path& Path); // to be added to path and cpath
|
void AddPath(const fs::path& Path); // to be added to path and cpath
|
||||||
void operator()() override;
|
void operator()() override;
|
||||||
@@ -253,6 +271,9 @@ private:
|
|||||||
sol::table Lua_FS_ListFiles(const std::string& Path);
|
sol::table Lua_FS_ListFiles(const std::string& Path);
|
||||||
sol::table Lua_FS_ListDirectories(const std::string& Path);
|
sol::table Lua_FS_ListDirectories(const std::string& Path);
|
||||||
|
|
||||||
|
prof::UnitProfileCollection mProfile {};
|
||||||
|
std::unordered_map<std::string, prof::TimePoint> mProfileStarts;
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
TLuaStateId mStateId;
|
TLuaStateId mStateId;
|
||||||
lua_State* mState;
|
lua_State* mState;
|
||||||
@@ -268,6 +289,7 @@ private:
|
|||||||
std::recursive_mutex mPathsMutex;
|
std::recursive_mutex mPathsMutex;
|
||||||
std::mt19937 mMersenneTwister;
|
std::mt19937 mMersenneTwister;
|
||||||
std::uniform_real_distribution<double> mUniformRealDistribution01;
|
std::uniform_real_distribution<double> mUniformRealDistribution01;
|
||||||
|
std::vector<sol::object> JsonStringToArray(JsonString Str);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TimedEvent {
|
struct TimedEvent {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#include "Profiling.h"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
prof::Duration prof::duration(const TimePoint& start, const TimePoint& end) {
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
prof::TimePoint prof::now() {
|
||||||
|
return std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
prof::Stats prof::UnitProfileCollection::stats(const std::string& unit) {
|
||||||
|
return m_map->operator[](unit).stats();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t prof::UnitProfileCollection::measurement_count(const std::string& unit) {
|
||||||
|
return m_map->operator[](unit).measurement_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void prof::UnitProfileCollection::add_sample(const std::string& unit, const Duration& duration) {
|
||||||
|
m_map->operator[](unit).add_sample(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
prof::Stats prof::UnitExecutionTime::stats() const {
|
||||||
|
std::unique_lock lock(m_mtx);
|
||||||
|
Stats result {};
|
||||||
|
// calculate sum
|
||||||
|
result.n = m_total_calls;
|
||||||
|
result.max = m_min;
|
||||||
|
result.min = m_max;
|
||||||
|
// calculate mean: mean = sum_x / n
|
||||||
|
result.mean = m_sum / double(m_total_calls);
|
||||||
|
// calculate stdev: stdev = sqrt((sum_x2 / n) - (mean * mean))
|
||||||
|
result.stdev = std::sqrt((m_measurement_sqr_sum / double(result.n)) - (result.mean * result.mean));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prof::UnitExecutionTime::add_sample(const Duration& dur) {
|
||||||
|
std::unique_lock lock(m_mtx);
|
||||||
|
m_sum += dur.count();
|
||||||
|
m_measurement_sqr_sum += dur.count() * dur.count();
|
||||||
|
m_min = std::min(dur.count(), m_min);
|
||||||
|
m_max = std::max(dur.count(), m_max);
|
||||||
|
++m_total_calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
prof::UnitExecutionTime::UnitExecutionTime() {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, prof::Stats> prof::UnitProfileCollection::all_stats() {
|
||||||
|
auto map = m_map.synchronize();
|
||||||
|
std::unordered_map<std::string, Stats> result {};
|
||||||
|
for (const auto& [name, time] : *map) {
|
||||||
|
result[name] = time.stats();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
size_t prof::UnitExecutionTime::measurement_count() const {
|
||||||
|
std::unique_lock lock(m_mtx);
|
||||||
|
return m_total_calls;
|
||||||
|
}
|
||||||
|
|
||||||
+102
-19
@@ -22,11 +22,13 @@
|
|||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
|
#include "Profiling.h"
|
||||||
#include "TLuaPlugin.h"
|
#include "TLuaPlugin.h"
|
||||||
#include "sol/object.hpp"
|
#include "sol/object.hpp"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <fmt/core.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -351,7 +353,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::EnqueueScript(TLuaStateId StateID, const
|
|||||||
return mLuaStates.at(StateID)->EnqueueScript(Script);
|
return mLuaStates.at(StateID)->EnqueueScript(Script);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
|
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaValue>& Args) {
|
||||||
std::unique_lock Lock(mLuaStatesMutex);
|
std::unique_lock Lock(mLuaStatesMutex);
|
||||||
return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args);
|
return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args);
|
||||||
}
|
}
|
||||||
@@ -360,17 +362,30 @@ void TLuaEngine::CollectAndInitPlugins() {
|
|||||||
if (!fs::exists(mResourceServerPath)) {
|
if (!fs::exists(mResourceServerPath)) {
|
||||||
fs::create_directories(mResourceServerPath);
|
fs::create_directories(mResourceServerPath);
|
||||||
}
|
}
|
||||||
for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) {
|
|
||||||
auto Path = Dir.path();
|
std::vector<fs::path> PluginsEntries;
|
||||||
Path = fs::relative(Path);
|
for (const auto& Entry : fs::directory_iterator(mResourceServerPath)) {
|
||||||
if (!Dir.is_directory()) {
|
if (Entry.is_directory()) {
|
||||||
beammp_error("\"" + Dir.path().string() + "\" is not a directory, skipping");
|
PluginsEntries.push_back(Entry);
|
||||||
} else {
|
} else {
|
||||||
TLuaPluginConfig Config { Path.stem().string() };
|
beammp_error("\"" + Entry.path().string() + "\" is not a directory, skipping");
|
||||||
FindAndParseConfig(Path, Config);
|
|
||||||
InitializePlugin(Path, Config);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::sort(PluginsEntries.begin(), PluginsEntries.end(), [](const fs::path& first, const fs::path& second) {
|
||||||
|
auto firstStr = first.string();
|
||||||
|
auto secondStr = second.string();
|
||||||
|
std::transform(firstStr.begin(), firstStr.end(), firstStr.begin(), ::tolower);
|
||||||
|
std::transform(secondStr.begin(), secondStr.end(), secondStr.begin(), ::tolower);
|
||||||
|
return firstStr < secondStr;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& Dir : PluginsEntries) {
|
||||||
|
auto Path = fs::relative(Dir);
|
||||||
|
TLuaPluginConfig Config { Path.stem().string() };
|
||||||
|
FindAndParseConfig(Path, Config);
|
||||||
|
InitializePlugin(Path, Config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config) {
|
void TLuaEngine::InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config) {
|
||||||
@@ -431,13 +446,50 @@ std::set<std::string> TLuaEngine::GetEventHandlersForState(const std::string& Ev
|
|||||||
return mLuaEvents[EventName][StateId];
|
return mLuaEvents[EventName][StateId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<sol::object> TLuaEngine::StateThreadData::JsonStringToArray(JsonString Str) {
|
||||||
|
auto LocalTable = Lua_JsonDecode(Str.value).as<std::vector<sol::object>>();
|
||||||
|
for (auto& value : LocalTable) {
|
||||||
|
if (value.is<std::string>() && value.as<std::string>() == BEAMMP_INTERNAL_NIL) {
|
||||||
|
value = sol::object {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LocalTable;
|
||||||
|
}
|
||||||
|
|
||||||
sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) {
|
||||||
auto Return = mEngine->TriggerEvent(EventName, mStateId, EventArgs);
|
auto Table = mStateView.create_table();
|
||||||
|
for (const sol::stack_proxy& Arg : EventArgs) {
|
||||||
|
switch (Arg.get_type()) {
|
||||||
|
case sol::type::none:
|
||||||
|
case sol::type::userdata:
|
||||||
|
case sol::type::lightuserdata:
|
||||||
|
case sol::type::thread:
|
||||||
|
case sol::type::function:
|
||||||
|
case sol::type::poly:
|
||||||
|
Table.add(BEAMMP_INTERNAL_NIL);
|
||||||
|
beammp_warnf("Passed a value of type '{}' to TriggerGlobalEvent(\"{}\", ...). This type can not be serialized, and cannot be passed between states. It will arrive as <nil> in handlers.", sol::type_name(EventArgs.lua_state(), Arg.get_type()), EventName);
|
||||||
|
break;
|
||||||
|
case sol::type::lua_nil:
|
||||||
|
Table.add(BEAMMP_INTERNAL_NIL);
|
||||||
|
break;
|
||||||
|
case sol::type::string:
|
||||||
|
case sol::type::number:
|
||||||
|
case sol::type::boolean:
|
||||||
|
case sol::type::table:
|
||||||
|
Table.add(Arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonString Str { LuaAPI::MP::JsonEncode(Table) };
|
||||||
|
beammp_debugf("json: {}", Str.value);
|
||||||
|
auto Return = mEngine->TriggerEvent(EventName, mStateId, Str);
|
||||||
auto MyHandlers = mEngine->GetEventHandlersForState(EventName, mStateId);
|
auto MyHandlers = mEngine->GetEventHandlersForState(EventName, mStateId);
|
||||||
|
|
||||||
|
sol::variadic_results LocalArgs = JsonStringToArray(Str);
|
||||||
for (const auto& Handler : MyHandlers) {
|
for (const auto& Handler : MyHandlers) {
|
||||||
auto Fn = mStateView[Handler];
|
auto Fn = mStateView[Handler];
|
||||||
if (Fn.valid()) {
|
if (Fn.valid()) {
|
||||||
auto LuaResult = Fn(EventArgs);
|
auto LuaResult = Fn(LocalArgs);
|
||||||
auto Result = std::make_shared<TLuaResult>();
|
auto Result = std::make_shared<TLuaResult>();
|
||||||
if (LuaResult.valid()) {
|
if (LuaResult.valid()) {
|
||||||
Result->Error = false;
|
Result->Error = false;
|
||||||
@@ -659,6 +711,7 @@ static void AddToTable(sol::table& table, const std::string& left, const T& valu
|
|||||||
static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) {
|
static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) {
|
||||||
switch (right.type()) {
|
switch (right.type()) {
|
||||||
case nlohmann::detail::value_t::null:
|
case nlohmann::detail::value_t::null:
|
||||||
|
AddToTable(table, left, sol::lua_nil_t {});
|
||||||
return;
|
return;
|
||||||
case nlohmann::detail::value_t::object: {
|
case nlohmann::detail::value_t::object: {
|
||||||
auto value = table.create();
|
auto value = table.create();
|
||||||
@@ -882,6 +935,30 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
UtilTable.set_function("RandomIntRange", [this](int64_t min, int64_t max) -> int64_t {
|
UtilTable.set_function("RandomIntRange", [this](int64_t min, int64_t max) -> int64_t {
|
||||||
return std::uniform_int_distribution(min, max)(mMersenneTwister);
|
return std::uniform_int_distribution(min, max)(mMersenneTwister);
|
||||||
});
|
});
|
||||||
|
UtilTable.set_function("DebugExecutionTime", [this]() -> sol::table {
|
||||||
|
sol::state_view StateView(mState);
|
||||||
|
sol::table Result = StateView.create_table();
|
||||||
|
auto stats = mProfile.all_stats();
|
||||||
|
for (const auto& [name, stat] : stats) {
|
||||||
|
Result[name] = StateView.create_table();
|
||||||
|
Result[name]["mean"] = stat.mean;
|
||||||
|
Result[name]["stdev"] = stat.stdev;
|
||||||
|
Result[name]["min"] = stat.min;
|
||||||
|
Result[name]["max"] = stat.max;
|
||||||
|
Result[name]["n"] = stat.n;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
});
|
||||||
|
UtilTable.set_function("DebugStartProfile", [this](const std::string& name) {
|
||||||
|
mProfileStarts[name] = prof::now();
|
||||||
|
});
|
||||||
|
UtilTable.set_function("DebugStopProfile", [this](const std::string& name) {
|
||||||
|
if (!mProfileStarts.contains(name)) {
|
||||||
|
beammp_lua_errorf("DebugStopProfile('{}') failed, because a profile for '{}' wasn't started", name, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mProfile.add_sample(name, prof::duration(mProfileStarts.at(name), prof::now()));
|
||||||
|
});
|
||||||
|
|
||||||
auto HttpTable = StateView.create_named_table("Http");
|
auto HttpTable = StateView.create_named_table("Http");
|
||||||
HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) {
|
HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) {
|
||||||
@@ -929,7 +1006,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) {
|
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaValue>& Args, const std::string& EventName, CallStrategy Strategy) {
|
||||||
// TODO: Document all this
|
// TODO: Document all this
|
||||||
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
|
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
|
||||||
if (Strategy == CallStrategy::BestEffort) {
|
if (Strategy == CallStrategy::BestEffort) {
|
||||||
@@ -951,7 +1028,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFrom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
|
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaValue>& Args) {
|
||||||
auto Result = std::make_shared<TLuaResult>();
|
auto Result = std::make_shared<TLuaResult>();
|
||||||
Result->StateId = mStateId;
|
Result->StateId = mStateId;
|
||||||
Result->Function = FunctionName;
|
Result->Function = FunctionName;
|
||||||
@@ -1019,6 +1096,7 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
std::chrono::milliseconds(500),
|
std::chrono::milliseconds(500),
|
||||||
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
||||||
if (NotExpired) {
|
if (NotExpired) {
|
||||||
|
auto ProfStart = prof::now();
|
||||||
auto TheQueuedFunction = std::move(mStateFunctionQueue.front());
|
auto TheQueuedFunction = std::move(mStateFunctionQueue.front());
|
||||||
mStateFunctionQueue.erase(mStateFunctionQueue.begin());
|
mStateFunctionQueue.erase(mStateFunctionQueue.begin());
|
||||||
Lock.unlock();
|
Lock.unlock();
|
||||||
@@ -1036,19 +1114,21 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (Arg.index()) {
|
switch (Arg.index()) {
|
||||||
case TLuaArgTypes_String:
|
case TLuaType::String:
|
||||||
LuaArgs.push_back(sol::make_object(StateView, std::get<std::string>(Arg)));
|
LuaArgs.push_back(sol::make_object(StateView, std::get<std::string>(Arg)));
|
||||||
break;
|
break;
|
||||||
case TLuaArgTypes_Int:
|
case TLuaType::Int:
|
||||||
LuaArgs.push_back(sol::make_object(StateView, std::get<int>(Arg)));
|
LuaArgs.push_back(sol::make_object(StateView, std::get<int>(Arg)));
|
||||||
break;
|
break;
|
||||||
case TLuaArgTypes_VariadicArgs:
|
case TLuaType::Json: {
|
||||||
LuaArgs.push_back(sol::make_object(StateView, std::get<sol::variadic_args>(Arg)));
|
auto LocalArgs = JsonStringToArray(std::get<JsonString>(Arg));
|
||||||
|
LuaArgs.insert(LuaArgs.end(), LocalArgs.begin(), LocalArgs.end());
|
||||||
break;
|
break;
|
||||||
case TLuaArgTypes_Bool:
|
}
|
||||||
|
case TLuaType::Bool:
|
||||||
LuaArgs.push_back(sol::make_object(StateView, std::get<bool>(Arg)));
|
LuaArgs.push_back(sol::make_object(StateView, std::get<bool>(Arg)));
|
||||||
break;
|
break;
|
||||||
case TLuaArgTypes_StringStringMap: {
|
case TLuaType::StringStringMap: {
|
||||||
auto Map = std::get<std::unordered_map<std::string, std::string>>(Arg);
|
auto Map = std::get<std::unordered_map<std::string, std::string>>(Arg);
|
||||||
auto Table = StateView.create_table();
|
auto Table = StateView.create_table();
|
||||||
for (const auto& [k, v] : Map) {
|
for (const auto& [k, v] : Map) {
|
||||||
@@ -1077,6 +1157,9 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
Result->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later
|
Result->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later
|
||||||
Result->MarkAsReady();
|
Result->MarkAsReady();
|
||||||
}
|
}
|
||||||
|
auto ProfEnd = prof::now();
|
||||||
|
auto ProfDuration = prof::duration(ProfStart, ProfEnd);
|
||||||
|
mProfile.add_sample(FnName, ProfDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user