lua_bind.h
#pragma once

#include <string>
#include <string_view>

#include "asio/detail/noncopyable.hpp"
#include "io_context_pool.h"
#include "logger.h"
#include "sol/sol.hpp"

namespace flower {
class lua_bind final : public asio::detail::noncopyable {
public:
    lua_bind() = default;

    ~lua_bind() = default;

    enum lua_msg_type {
        lmt_none,
        lmt_main,
        lmt_message,
        lmt_timer,
        lmt_mysql,
        lmt_server_stop,
        lmt_hotfix,
    };

    bool start(flower::io_context_pool& pool, std::string_view script_dir);

    void call_code(std::string_view code);

    void call_file(std::string_view file);

    template <typename... Args>
    void call_frame(Args&&... args) {
        auto res = state_["on_frame"](std::forward<Args>(args)...);
        if (!res.valid()) {
            sol::error err = res;
            LOG_ERROR("lua_bind::call_frame error:\n{}", err.what());
        }
    }

    sol::state& get() {
        return state_;
    }

private:
    void register_class();

    bool register_field(std::string_view script_path);

private:
    sol::state state_;
    std::string script_path_{};
    flower::io_context_pool* io_pool_{};
};
}  // namespace flower
lua_bind.cpp
#include "lua_bind.h"

#include "packet.h"
#include "comm_func.h"

namespace flower {
bool lua_bind::start(flower::io_context_pool& pool, std::string_view script_dir) {
    try {
        state_.open_libraries();
        if (!register_field(script_dir)) {
            return false;
        }

        register_class();
        io_pool_ = &pool;
    } catch (const sol::error& e) {
        LOG_ERROR("lua_bind::start error:{}", e.what());
    }

    return true;
}

void lua_bind::call_code(std::string_view code) {
    state_.script(code, [](lua_State* L, sol::protected_function_result pfr) {
        sol::error err = pfr;
        LOG_ERROR("lua_bind::call_code error:{}", err.what());
        return pfr;
    });
}

void lua_bind::call_file(std::string_view file) {
    std::string file_path = script_path_;
    file_path.append(file.data()).append(".lua");
    state_.script_file(file_path, [](lua_State* L, sol::protected_function_result pfr) {
        sol::error err = pfr;
        LOG_ERROR("lua_bind::call_file error:{}", err.what());
        return pfr;
    });
}

void lua_bind::register_class() {
    using namespace flower;

    auto packet = state_.new_usertype<flower::packet>("packet");
    packet["read_int8"] = &packet::read_int8;
    packet["read_uint8"] = &packet::read_uint8;
    packet["read_int16"] = &packet::read_int16;
    packet["read_uint16"] = &packet::read_uint16;
    packet["read_int32"] = &packet::read_int32;
    packet["read_uint32"] = &packet::read_uint32;
    packet["read_int64"] = &packet::read_int64;
    packet["read_uint64"] = &packet::read_uint64;
    packet["read_float"] = &packet::read_float;
    packet["read_double"] = &packet::read_double;
    packet["read_string"] = &packet::read_string;
    packet["can_read_more"] = &packet::can_read_more;
}

bool lua_bind::register_field(std::string_view script_path) {
    using namespace flower;

    std::string path = comm_func::get_current_exe_dir();
    if (path.empty()) {
        LOG_ERROR("lua_bind::register_lua_field exec_path is empty");
        return false;
    }

    if (script_path.empty()) {
        LOG_ERROR("lua_bind::register_lua_field script_path is empty");
        return false;
    }

#ifdef _WIN32
    auto pos = path.find_last_of("\\");
#else
    auto pos = path.find_last_of('/');
#endif

    if (pos == std::string::npos) {
        LOG_ERROR("lua_bind::register_lua_field path error");
        return false;
    }

    script_path_ = path.substr(0, pos + 1) + script_path.data();

#ifdef _WIN32
    script_path_.append("\\");
#else
    script_path_.append("/");
#endif

    state_["lExecPath"] = path;
    state_["lScriptPath"] = script_path_;
    state_["print"] = [](lua_State* L) {
        std::string msg;
        int n = lua_gettop(L);
        int i;
        lua_getglobal(L, "tostring");
        for (i = 1; i <= n; i++) {
            std::size_t l;
            lua_pushvalue(L, -1);
            lua_pushvalue(L, i);
            lua_call(L, 1, 1);
            const char* str = lua_tolstring(L, -1, &l);
            if (str == nullptr) {
                LOG_ERROR("[LUA] 'tostring' must return a string to 'print'");
                return 0;
            }
            if (i > 1) {
                msg.append("\t");
            }
            msg.append(str);
            lua_pop(L, 1);
        }
        LOG_DEBUG("[LUA] {}", msg);
        return 0;
    };

    state_["lShowMem"] = [this]() {
        state_.step_gc(state_.memory_used());
        LOG_WARN("LUA_MEM: {:.2f}M", static_cast<double>(state_.memory_used()) / 1024 / 1024);
    };

    state_["lShowInfo"] = [](std::string_view msg) {
        LOG_INFO("[LUA] {}", msg);
    };

    state_["lShowError"] = [](std::string_view msg) {
        LOG_ERROR("[LUA] {}", msg);
    };

    state_["lGetMilliTime"] = comm_func::get_milli_time;

    state_["lGetMicroTime"] = comm_func::get_micro_time;

    state_["lGetSecTime"] = comm_func::get_sec_time;

    state_["lAddTimer"] = [this](uint32_t delay, uint32_t interval, int32_t count) {
        return io_pool_->add_timer(delay, interval, count, [this](timer_event::ptr event) {
            call_frame(lua_msg_type::lmt_timer, event->get_sn());
        });
    };

    state_["lDelTimer"] = [this](int32_t sn) {
        io_pool_->del_timer(sn);
    };

    return true;
}
}  // namespace flower