#include "StarUtilityLuaBindings.hpp" #include "StarJsonExtra.hpp" #include "StarLuaGameConverters.hpp" #include "StarUuid.hpp" #include "StarRandom.hpp" #include "StarPerlin.hpp" #include "StarXXHash.hpp" #include "StarLogging.hpp" #include "StarInterpolation.hpp" namespace Star { template <> struct LuaConverter : LuaUserDataConverter {}; template <> struct LuaUserDataMethods { static LuaMethods make() { LuaMethods methods; methods.registerMethod("init", [](RandomSource& randomSource, Maybe seed) { if (seed) randomSource.init(*seed); else randomSource.init(); }); methods.registerMethod("addEntropy", [](RandomSource& randomSource, Maybe seed) { if (seed) randomSource.addEntropy(*seed); else randomSource.addEntropy(); }); methods.registerMethodWithSignature("randu32", mem_fn(&RandomSource::randu32)); methods.registerMethodWithSignature("randu64", mem_fn(&RandomSource::randu64)); methods.registerMethodWithSignature("randi32", mem_fn(&RandomSource::randi32)); methods.registerMethodWithSignature("randi64", mem_fn(&RandomSource::randi64)); methods.registerMethod("randf", [](RandomSource& randomSource, Maybe arg1, Maybe arg2) { if (arg1 && arg2) return randomSource.randf(*arg1, *arg2); else return randomSource.randf(); }); methods.registerMethod("randd", [](RandomSource& randomSource, Maybe arg1, Maybe arg2) { if (arg1 && arg2) return randomSource.randd(*arg1, *arg2); else return randomSource.randd(); }); methods.registerMethodWithSignature("randb", mem_fn(&RandomSource::randb)); methods.registerMethod("randInt", [](RandomSource& randomSource, int64_t arg1, Maybe arg2) { if (arg2) return randomSource.randInt(arg1, *arg2); else return randomSource.randInt(arg1); }); methods.registerMethod("randUInt", [](RandomSource& randomSource, uint64_t arg1, Maybe arg2) { if (arg2) return randomSource.randUInt(arg1, *arg2); else return randomSource.randUInt(arg1); }); return methods; } }; template <> struct LuaConverter : LuaUserDataConverter {}; template <> struct LuaUserDataMethods { static LuaMethods make() { LuaMethods methods; methods.registerMethod("get", [](PerlinF& perlinF, float x, Maybe y, Maybe z) { if (y && z) return perlinF.get(x, *y, *z); else if (y) return perlinF.get(x, *y); else return perlinF.get(x); }); return methods; } }; String LuaBindings::formatLua(String const& string, List const& args) { auto argsIt = args.begin(); auto argsEnd = args.end(); auto popArg = [&argsIt, &argsEnd]() -> LuaValue { if (argsIt == argsEnd) return LuaNil; return *argsIt++; }; auto stringIt = string.begin(); auto stringEnd = string.end(); String result; while (stringIt != stringEnd) { if (*stringIt == '%') { auto next = stringIt; ++next; if (next == stringEnd) throw StarException("No specifier following '%'"); else if (*next == '%') result += '%'; else if (*next == 's') result += toString(popArg()); else throw StarException::format("Improper lua log format specifier {}", (char)*next); ++next; stringIt = next; } else { result += *stringIt++; } } return result; } LuaCallbacks LuaBindings::makeUtilityCallbacks() { LuaCallbacks callbacks; callbacks.registerCallback("nrand", UtilityCallbacks::nrand); callbacks.registerCallback("makeUuid", UtilityCallbacks::makeUuid); callbacks.registerCallback("logInfo", UtilityCallbacks::logInfo); callbacks.registerCallback("logWarn", UtilityCallbacks::logWarn); callbacks.registerCallback("logError", UtilityCallbacks::logError); callbacks.registerCallback("setLogMap", UtilityCallbacks::setLogMap); callbacks.registerCallback("printJson", UtilityCallbacks::printJson); callbacks.registerCallback("print", UtilityCallbacks::print); callbacks.registerCallback("interpolateSinEase", UtilityCallbacks::interpolateSinEase); callbacks.registerCallback("replaceTags", UtilityCallbacks::replaceTags); callbacks.registerCallback("jsonMerge", [](Json const& a, Json const& b) { return jsonMerge(a, b); }); callbacks.registerCallback("jsonQuery", [](Json const& json, String const& path, Json const& def) { return json.query(path, def); }); callbacks.registerCallback("makeRandomSource", [](Maybe seed) { if (seed) return RandomSource(*seed); else return RandomSource(); }); callbacks.registerCallback("makePerlinSource", [](Json const& config) { return PerlinF(config); }); auto hash64LuaValues = [](LuaVariadic const& values) -> uint64_t { XXHash64 hash; for (auto const& value : values) { if (auto b = value.ptr()) xxHash64Push(hash, *b); else if (auto i = value.ptr()) xxHash64Push(hash, *i); else if (auto f = value.ptr()) xxHash64Push(hash, *f); else if (auto s = value.ptr()) xxHash64Push(hash, s->ptr()); else throw LuaException("Unhashable lua type passed to staticRandomXX binding"); } return hash.digest(); }; callbacks.registerCallback("staticRandomI32", [hash64LuaValues](LuaVariadic const& hashValues) { return (int32_t)hash64LuaValues(hashValues); }); callbacks.registerCallback("staticRandomI32Range", [hash64LuaValues](int32_t min, int32_t max, LuaVariadic const& hashValues) { if (max < min) throw LuaException("Maximum bound in staticRandomI32Range must be >= minimum bound!"); uint64_t denom = (uint64_t)(-1) / ((uint64_t)(max - min) + 1); return (int32_t)(hash64LuaValues(hashValues) / denom + min); }); callbacks.registerCallback("staticRandomDouble", [hash64LuaValues](LuaVariadic const& hashValues) { return (hash64LuaValues(hashValues) & 0x7fffffffffffffff) / 9223372036854775808.0; }); callbacks.registerCallback("staticRandomDoubleRange", [hash64LuaValues](double min, double max, LuaVariadic const& hashValues) { if (max < min) throw LuaException("Maximum bound in staticRandomDoubleRange must be >= minimum bound!"); return (hash64LuaValues(hashValues) & 0x7fffffffffffffff) / 9223372036854775808.0 * (max - min) + min; }); return callbacks; } double LuaBindings::UtilityCallbacks::nrand(Maybe const& stdev, Maybe const& mean) { return Random::nrandd(stdev.value(1.0), mean.value(0)); } String LuaBindings::UtilityCallbacks::makeUuid() { return Uuid().hex(); } void LuaBindings::UtilityCallbacks::logInfo(String const& str, LuaVariadic const& args) { Logger::log(LogLevel::Info, formatLua(str, args).utf8Ptr()); } void LuaBindings::UtilityCallbacks::logWarn(String const& str, LuaVariadic const& args) { Logger::log(LogLevel::Warn, formatLua(str, args).utf8Ptr()); } void LuaBindings::UtilityCallbacks::logError(String const& str, LuaVariadic const& args) { Logger::log(LogLevel::Error, formatLua(str, args).utf8Ptr()); } void LuaBindings::UtilityCallbacks::setLogMap(String const& key, String const& value, LuaVariadic const& args) { LogMap::set(key, formatLua(value, args)); } String LuaBindings::UtilityCallbacks::printJson(Json const& arg, Maybe pretty) { return arg.repr(pretty.value()); } String LuaBindings::UtilityCallbacks::print(LuaValue const& value) { return toString(value); } LuaValue LuaBindings::UtilityCallbacks::interpolateSinEase(LuaEngine& engine, double offset, LuaValue const& value1, LuaValue const& value2) { if (auto floatValue1 = engine.luaMaybeTo(value1)) { auto floatValue2 = engine.luaMaybeTo(value2); return sinEase(offset, *floatValue1, *floatValue2); } else { return engine.luaFrom(sinEase(offset, engine.luaTo(value1), engine.luaTo(value2))); } } String LuaBindings::UtilityCallbacks::replaceTags(String const& str, StringMap const& tags) { return str.replaceTags(tags); } }