<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_TEMPORARY_BINARY_EXPR_HPP
#define ETL_TEMPORARY_BINARY_EXPR_HPP

#include <iostream>     //For stream support
#include <ostream>
#include <memory>       //For shared_ptr

#include "traits_lite.hpp"
#include "iterator.hpp"
#include "tmp.hpp"

// CRTP classes
#include "crtp/comparable.hpp"
#include "crtp/iterable.hpp"

namespace etl {

template <typename D, typename V>
struct temporary_expr : comparable<D>, iterable<D> {
    using         derived_t = D;
    using        value_type = V;
    using       memory_type = value_type*;
    using const_memory_type = const value_type*;

    derived_t& as_derived() noexcept {
        return *static_cast<derived_t*>(this);
    }

    const derived_t& as_derived() const noexcept {
        return *static_cast<const derived_t*>(this);
    }

    //Apply the expression

    value_type operator[](std::size_t i) const {
        return as_derived().result()[i];
    }

    template<typename... S, cpp_enable_if(sizeof...(S) == sub_size_compare<derived_t>::value)>
    value_type operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return as_derived().result()(args...);
    }

    template<typename DD = D, cpp_enable_if((sub_size_compare<DD>::value > 1))>
    auto operator()(std::size_t i) const {
        return sub(as_derived(), i);
    }

    //{{{ Iterator

    iterator<const derived_t> begin() const noexcept {
        return {as_derived(), 0};
    }

    iterator<const derived_t> end() const noexcept {
        return {as_derived(), size(as_derived())};
    }

    // Direct memory access

    memory_type memory_start() noexcept {
        return as_derived().result().memory_start();
    }

    const_memory_type memory_start() const noexcept {
        return as_derived().result().memory_start();
    }

    memory_type memory_end() noexcept {
        return as_derived().result().memory_end();
    }

    const_memory_type memory_end() const noexcept {
        return as_derived().result().memory_end();
    }
};

template <typename T, typename AExpr, typename Op, typename Forced>
struct temporary_unary_expr final : temporary_expr<temporary_unary_expr<T, AExpr, Op, Forced>, T> {
    using        value_type = T;
    using       result_type = std::conditional_t<std::is_same<Forced, void>::value, typename Op::template result_type<AExpr>, Forced>;
    using         data_type = std::conditional_t<std::is_same<Forced, void>::value, std::shared_ptr<result_type>, result_type>;

private:
    static_assert(is_etl_expr<AExpr>::value, "The argument must be an ETL expr");

    using this_type = temporary_unary_expr<T, AExpr, Op, Forced>;

    AExpr _a;
    data_type _c;
    bool evaluated = false;
    bool allocated = false;

public:
    //Construct a new expression
    explicit temporary_unary_expr(AExpr a) : _a(a) {
        //Nothing else to init
    }

    //Construct a new expression
    temporary_unary_expr(AExpr a, std::conditional_t<std::is_same<Forced,void>::value, int, Forced> c) : _a(a), _c(c) {
        //Nothing else to init
    }

    //Copy an expression
    temporary_unary_expr(const temporary_unary_expr& e) : _a(e._a), _c(e._c) {
        //Nothing else to init
    }

    //Move an expression
    temporary_unary_expr(temporary_unary_expr&& e) : _a(e._a), _c(optional_move<std::is_same<Forced,void>::value>(e._c)), evaluated(e.evaluated) {
        e.evaluated = false;
    }

    //Expressions are invariant
    temporary_unary_expr& operator=(const temporary_unary_expr& /*e*/) = delete;
    temporary_unary_expr& operator=(temporary_unary_expr&& /*e*/) = delete;

    //Accessors

    std::add_lvalue_reference_t<AExpr> a(){
        return _a;
    }

    cpp::add_const_lvalue_t<AExpr> a() const {
        return _a;
    }

    void evaluate(){
        if(!evaluated){
            Op::apply(_a, result());
            evaluated = true;
        }
    }

    template<typename Result, typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& r){
        evaluate();
        r = result();
    }

    template<typename Result, typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& result){
        Op::apply(_a, std::forward<Result>(result));
    }

    template<typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void allocate_temporary() const {
        allocated = true;
    }

    template<typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void allocate_temporary(){
        if(!_c){
            _c.reset(Op::allocate(_a));
        }

        allocated = true;
    }

    using get_result_op = std::conditional_t<std::is_same<Forced, void>::value, dereference_op, forward_op>;

    result_type& result(){
        cpp_assert(evaluated, "The result has not been evaluated");
        cpp_assert(allocated, "The result has not been allocated");
        return get_result_op::apply(_c);
    }

    const result_type& result() const {
        cpp_assert(evaluated, "The result has not been evaluated");
        cpp_assert(allocated, "The result has not been allocated");
        return get_result_op::apply(_c);
    }
};

template <typename T, typename AExpr, typename BExpr, typename Op, typename Forced>
struct temporary_binary_expr final : temporary_expr<temporary_binary_expr<T, AExpr, BExpr, Op, Forced>, T> {
    using        value_type = T;
    using       result_type = std::conditional_t<std::is_same<Forced, void>::value, typename Op::template result_type<AExpr, BExpr>, Forced>;
    using         data_type = std::conditional_t<std::is_same<Forced, void>::value, std::shared_ptr<result_type>, result_type>;

private:
    static_assert(is_etl_expr<AExpr>::value && is_etl_expr<BExpr>::value, "Both arguments must be ETL expr");

    using this_type = temporary_binary_expr<T, AExpr, BExpr, Op, Forced>;

    AExpr _a;
    BExpr _b;
    data_type _c;
    bool evaluated = false;

public:
    //Construct a new expression
    temporary_binary_expr(AExpr a, BExpr b) : _a(a), _b(b) {
        //Nothing else to init
    }

    //Construct a new expression
    temporary_binary_expr(AExpr a, BExpr b, std::conditional_t<std::is_same<Forced,void>::value, int, Forced> c) : _a(a), _b(b), _c(c) {
        //Nothing else to init
    }

    //Copy an expression
    temporary_binary_expr(const temporary_binary_expr& e) : _a(e._a), _b(e._b), _c(e._c) {
        //Nothing else to init
    }

    //Move an expression
    temporary_binary_expr(temporary_binary_expr&& e) : _a(e._a), _b(e._b), _c(optional_move<std::is_same<Forced,void>::value>(e._c)), evaluated(e.evaluated) {
        e.evaluated = false;
    }

    //Expressions are invariant
    temporary_binary_expr& operator=(const temporary_binary_expr& /*e*/) = delete;
    temporary_binary_expr& operator=(temporary_binary_expr&& /*e*/) = delete;

    //Accessors

    std::add_lvalue_reference_t<AExpr> a(){
        return _a;
    }

    cpp::add_const_lvalue_t<AExpr> a() const {
        return _a;
    }

    std::add_lvalue_reference_t<BExpr> b(){
        return _b;
    }

    cpp::add_const_lvalue_t<BExpr> b() const {
        return _b;
    }

    void evaluate(){
        if(!evaluated){
            Op::apply(_a, _b, result());
            evaluated = true;
        }
    }

    template<typename Result, typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& r){
        evaluate();

        //TODO Normally, this should not be necessary
        if(&r != &result()){
            r = result();
        }
    }

    template<typename Result, typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& result){
        Op::apply(_a, _b, std::forward<Result>(result));
    }

    template<typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void allocate_temporary() const {
        //NOP
    }

    template<typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void allocate_temporary(){
        if(!_c){
            _c.reset(Op::allocate(_a, _b));
        }
    }

    using get_result_op = std::conditional_t<std::is_same<Forced, void>::value, dereference_op, forward_op>;

    result_type& result(){
        return get_result_op::apply(_c);
    }

    const result_type& result() const {
        return get_result_op::apply(_c);
    }
};

template <typename T, typename AExpr, typename Op, typename Forced>
std::ostream& operator<<(std::ostream& os, const temporary_unary_expr<T, AExpr, Op, Forced>& expr){
    return os << Op::desc() << "(" << expr.a() << ")";
}

template <typename T, typename AExpr, typename BExpr, typename Op, typename Forced>
std::ostream& operator<<(std::ostream& os, const temporary_binary_expr<T, AExpr, BExpr, Op, Forced>& expr){
    return os << Op::desc() << "(" << expr.a() << ", " << expr.b() << ")";
}

} //end of namespace etl

#endif
<commit_msg>Add assertions<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_TEMPORARY_BINARY_EXPR_HPP
#define ETL_TEMPORARY_BINARY_EXPR_HPP

#include <iostream>     //For stream support
#include <ostream>
#include <memory>       //For shared_ptr

#include "traits_lite.hpp"
#include "iterator.hpp"
#include "tmp.hpp"

// CRTP classes
#include "crtp/comparable.hpp"
#include "crtp/iterable.hpp"

namespace etl {

template <typename D, typename V>
struct temporary_expr : comparable<D>, iterable<D> {
    using         derived_t = D;
    using        value_type = V;
    using       memory_type = value_type*;
    using const_memory_type = const value_type*;

    derived_t& as_derived() noexcept {
        return *static_cast<derived_t*>(this);
    }

    const derived_t& as_derived() const noexcept {
        return *static_cast<const derived_t*>(this);
    }

    //Apply the expression

    value_type operator[](std::size_t i) const {
        return as_derived().result()[i];
    }

    template<typename... S, cpp_enable_if(sizeof...(S) == sub_size_compare<derived_t>::value)>
    value_type operator()(S... args) const {
        static_assert(cpp::all_convertible_to<std::size_t, S...>::value, "Invalid size types");

        return as_derived().result()(args...);
    }

    template<typename DD = D, cpp_enable_if((sub_size_compare<DD>::value > 1))>
    auto operator()(std::size_t i) const {
        return sub(as_derived(), i);
    }

    //{{{ Iterator

    iterator<const derived_t> begin() const noexcept {
        return {as_derived(), 0};
    }

    iterator<const derived_t> end() const noexcept {
        return {as_derived(), size(as_derived())};
    }

    // Direct memory access

    memory_type memory_start() noexcept {
        return as_derived().result().memory_start();
    }

    const_memory_type memory_start() const noexcept {
        return as_derived().result().memory_start();
    }

    memory_type memory_end() noexcept {
        return as_derived().result().memory_end();
    }

    const_memory_type memory_end() const noexcept {
        return as_derived().result().memory_end();
    }
};

template <typename T, typename AExpr, typename Op, typename Forced>
struct temporary_unary_expr final : temporary_expr<temporary_unary_expr<T, AExpr, Op, Forced>, T> {
    using        value_type = T;
    using       result_type = std::conditional_t<std::is_same<Forced, void>::value, typename Op::template result_type<AExpr>, Forced>;
    using         data_type = std::conditional_t<std::is_same<Forced, void>::value, std::shared_ptr<result_type>, result_type>;

private:
    static_assert(is_etl_expr<AExpr>::value, "The argument must be an ETL expr");

    using this_type = temporary_unary_expr<T, AExpr, Op, Forced>;

    AExpr _a;
    data_type _c;
    bool evaluated = false;
    bool allocated = false;

public:
    //Construct a new expression
    explicit temporary_unary_expr(AExpr a) : _a(a) {
        //Nothing else to init
    }

    //Construct a new expression
    temporary_unary_expr(AExpr a, std::conditional_t<std::is_same<Forced,void>::value, int, Forced> c) : _a(a), _c(c) {
        //Nothing else to init
    }

    //Copy an expression
    temporary_unary_expr(const temporary_unary_expr& e) : _a(e._a), _c(e._c) {
        //Nothing else to init
    }

    //Move an expression
    temporary_unary_expr(temporary_unary_expr&& e) : _a(e._a), _c(optional_move<std::is_same<Forced,void>::value>(e._c)), evaluated(e.evaluated) {
        e.evaluated = false;
    }

    //Expressions are invariant
    temporary_unary_expr& operator=(const temporary_unary_expr& /*e*/) = delete;
    temporary_unary_expr& operator=(temporary_unary_expr&& /*e*/) = delete;

    //Accessors

    std::add_lvalue_reference_t<AExpr> a(){
        return _a;
    }

    cpp::add_const_lvalue_t<AExpr> a() const {
        return _a;
    }

    void evaluate(){
        if(!evaluated){
            Op::apply(_a, result());
            evaluated = true;
        }
    }

    template<typename Result, typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& r){
        evaluate();
        r = result();
    }

    template<typename Result, typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& result){
        Op::apply(_a, std::forward<Result>(result));
    }

    template<typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void allocate_temporary() const {
        allocated = true;
    }

    template<typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void allocate_temporary(){
        if(!_c){
            _c.reset(Op::allocate(_a));
        }

        allocated = true;
    }

    using get_result_op = std::conditional_t<std::is_same<Forced, void>::value, dereference_op, forward_op>;

    result_type& result(){
        cpp_assert(evaluated, "The result has not been evaluated");
        cpp_assert(allocated, "The result has not been allocated");
        return get_result_op::apply(_c);
    }

    const result_type& result() const {
        cpp_assert(evaluated, "The result has not been evaluated");
        cpp_assert(allocated, "The result has not been allocated");
        return get_result_op::apply(_c);
    }
};

template <typename T, typename AExpr, typename BExpr, typename Op, typename Forced>
struct temporary_binary_expr final : temporary_expr<temporary_binary_expr<T, AExpr, BExpr, Op, Forced>, T> {
    using        value_type = T;
    using       result_type = std::conditional_t<std::is_same<Forced, void>::value, typename Op::template result_type<AExpr, BExpr>, Forced>;
    using         data_type = std::conditional_t<std::is_same<Forced, void>::value, std::shared_ptr<result_type>, result_type>;

private:
    static_assert(is_etl_expr<AExpr>::value && is_etl_expr<BExpr>::value, "Both arguments must be ETL expr");

    using this_type = temporary_binary_expr<T, AExpr, BExpr, Op, Forced>;

    AExpr _a;
    BExpr _b;
    data_type _c;
    bool evaluated = false;
    bool allocated = false;

public:
    //Construct a new expression
    temporary_binary_expr(AExpr a, BExpr b) : _a(a), _b(b) {
        //Nothing else to init
    }

    //Construct a new expression
    temporary_binary_expr(AExpr a, BExpr b, std::conditional_t<std::is_same<Forced,void>::value, int, Forced> c) : _a(a), _b(b), _c(c) {
        //Nothing else to init
    }

    //Copy an expression
    temporary_binary_expr(const temporary_binary_expr& e) : _a(e._a), _b(e._b), _c(e._c) {
        //Nothing else to init
    }

    //Move an expression
    temporary_binary_expr(temporary_binary_expr&& e) : _a(e._a), _b(e._b), _c(optional_move<std::is_same<Forced,void>::value>(e._c)), evaluated(e.evaluated) {
        e.evaluated = false;
    }

    //Expressions are invariant
    temporary_binary_expr& operator=(const temporary_binary_expr& /*e*/) = delete;
    temporary_binary_expr& operator=(temporary_binary_expr&& /*e*/) = delete;

    //Accessors

    std::add_lvalue_reference_t<AExpr> a(){
        return _a;
    }

    cpp::add_const_lvalue_t<AExpr> a() const {
        return _a;
    }

    std::add_lvalue_reference_t<BExpr> b(){
        return _b;
    }

    cpp::add_const_lvalue_t<BExpr> b() const {
        return _b;
    }

    void evaluate(){
        if(!evaluated){
            Op::apply(_a, _b, result());
            evaluated = true;
        }
    }

    template<typename Result, typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& r){
        evaluate();

        //TODO Normally, this should not be necessary
        if(&r != &result()){
            r = result();
        }
    }

    template<typename Result, typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void direct_evaluate(Result&& result){
        Op::apply(_a, _b, std::forward<Result>(result));
    }

    template<typename F = Forced, cpp_disable_if(std::is_same<F, void>::value)>
    void allocate_temporary() const {
        allocated = true;
    }

    template<typename F = Forced, cpp_enable_if(std::is_same<F, void>::value)>
    void allocate_temporary(){
        if(!_c){
            _c.reset(Op::allocate(_a, _b));
        }

        allocated = true;
    }

    using get_result_op = std::conditional_t<std::is_same<Forced, void>::value, dereference_op, forward_op>;

    result_type& result(){
        cpp_assert(evaluated, "The result has not been evaluated");
        cpp_assert(allocated, "The result has not been allocated");
        return get_result_op::apply(_c);
    }

    const result_type& result() const {
        cpp_assert(evaluated, "The result has not been evaluated");
        cpp_assert(allocated, "The result has not been allocated");
        return get_result_op::apply(_c);
    }
};

template <typename T, typename AExpr, typename Op, typename Forced>
std::ostream& operator<<(std::ostream& os, const temporary_unary_expr<T, AExpr, Op, Forced>& expr){
    return os << Op::desc() << "(" << expr.a() << ")";
}

template <typename T, typename AExpr, typename BExpr, typename Op, typename Forced>
std::ostream& operator<<(std::ostream& os, const temporary_binary_expr<T, AExpr, BExpr, Op, Forced>& expr){
    return os << Op::desc() << "(" << expr.a() << ", " << expr.b() << ")";
}

} //end of namespace etl

#endif
<|endoftext|>


<commit_before>#include "entity_system.h"
#include "cmapclient.h"
#include "enumerate.h"
#include "itemdb.h"
#include <entt.hpp>
#include "components/basic_info.h"
#include "components/client.h"
#include "components/computed_values.h"
#include "components/faction.h"
#include "components/character_graphics.h"
#include "components/guild.h"
#include "components/hotbar.h"
#include "components/inventory.h"
#include "components/item.h"
#include "components/level.h"
#include "components/life.h"
#include "components/magic.h"
#include "components/npc.h"
#include "components/owner.h"
#include "components/position.h"
#include "components/skills.h"
#include "components/stamina.h"
#include "components/stats.h"
#include "components/status_effects.h"
#include "components/wishlist.h"

#include "chat/normal_chat.h"

EntitySystem::EntitySystem(std::chrono::milliseconds maxTimePerUpdate) : maxTimePerUpdate(maxTimePerUpdate), nearby(*this) {
	logger = Core::CLog::GetLogger(Core::log_type::GENERAL).lock();

    // dispatcher registration
    register_dispatcher(std::function{Chat::normal_chat});
}

void EntitySystem::stop() {
    work_queue.kill();
}

bool EntitySystem::dispatch_packet(RoseCommon::Entity entity, std::unique_ptr<RoseCommon::CRosePacket>&& packet) {
    if (!packet) {
        return false;
    }
    if (!dispatcher.is_supported(*packet.get())) {
        return false;
    }
    add_task(std::move([this, entity, packet = std::move(packet)](EntitySystem& entitySystem) mutable {
        dispatcher.dispatch(entitySystem, entity, std::move(packet));
    }));
    return true;
}

void EntitySystem::run() {
    for (auto [res, task] = work_queue.pop_front(); res;) {
        {
            std::lock_guard<std::mutex> lock(access);
            std::invoke(std::move(task), *this);
        }
        auto [tmp_res, tmp_task] = work_queue.pop_front();
        res = tmp_res;
        task = std::move(tmp_task);
    }
}

void EntitySystem::send_map(const RoseCommon::CRosePacket& packet) {
    registry.view<Component::Client>().each([](auto entity, auto &client_ptr) {
        (void)entity;
        if (auto client = client_ptr.client.lock()) {
            client->send(packet);
        }
    });
}

void EntitySystem::send_nearby(RoseCommon::Entity entity, const RoseCommon::CRosePacket& packet) {
    registry.view<Component::Client>().each([entity, this](auto other, auto &client_ptr) {
        if (!nearby.is_nearby(entity, other)) return;
        if (auto client = client_ptr.client.lock()) {
            client->send(packet);
        }
    });
}

void EntitySystem::sent_to(RoseCommon::Entity entity, const RoseCommon::CRosePacket& packet) {
    if (auto client_ptr = registry.try_get<Component::Client>(entity)) {
        if (auto client = client_ptr->client.lock()) {
            client->send(packet);
        }
    }
}

void EntitySystem::delete_entity(RoseCommon::Entity entity) {
    add_task([entity](EntitySystem& entitySystem) {
        entitySystem.registry.destroy(entity);
    });
}

RoseCommon::Entity EntitySystem::load_character(uint32_t charId, bool platinium, uint32_t sessionId, std::weak_ptr<CMapClient> client) {
    using namespace Component;
    auto conn = Core::connectionPool.getConnection(Core::osirose);
    Core::CharacterTable characters{};
    Core::InventoryTable inventoryTable{};
    Core::SkillTable skillsTable{};
    Core::WishTable wish{};

    auto charRes = conn(sqlpp::select(sqlpp::count(characters.id), sqlpp::all_of(characters))
                          .from(characters).where(characters.id == charId));

    if (static_cast<long>(charRes.front().count) != 1L) {
        return entt::null;
    }
    const auto& charRow = charRes.front();

    entt::prototype prototype(registry);

    auto& basicInfo = prototype.set<BasicInfo>();
    basicInfo.name = charRow.name;
    basicInfo.id = idManager.get_free_id();
    basicInfo.tag = sessionId;
    basicInfo.teamId = basicInfo.id;
    basicInfo.job = charRow.job;
    basicInfo.statPoints = charRow.statPoints;
    basicInfo.skillPoints = charRow.skillPoints;
    basicInfo.pkFlag = charRow.pkFlag;
    basicInfo.stone = charRow.stone;

    auto& component_client = prototype.set<Client>();
    component_client.client = client;

    auto& computedValues = prototype.set<ComputedValues>();
    computedValues.command = RoseCommon::Command::STOP;
    computedValues.isOnMap.store(false);
    computedValues.moveMode = RoseCommon::MoveMode::WALK;
    computedValues.runSpeed = 0;
    computedValues.atkSpeed = 0;
    computedValues.weightRate = 0;

    auto& faction = prototype.set<Faction>();
    faction.id = charRow.factionid;
    faction.rank = charRow.factionRank;
    faction.fame = charRow.fame;
    faction.factionFame[0] = charRow.factionFame1;
    faction.factionFame[1] = charRow.factionFame2;
    faction.points[0] = charRow.factionPoints1;
    faction.points[1] = charRow.factionPoints2;
    faction.points[2] = charRow.factionPoints3;

    auto& characterGraphics = prototype.set<CharacterGraphics>();
    characterGraphics.face = charRow.face;
    characterGraphics.hair = charRow.hair;
    characterGraphics.race = charRow.race;

    auto& guild = prototype.set<Guild>();
    guild.id = charRow.clanid;
    guild.contribution = charRow.clanContribution;
    guild.rank = charRow.clanRank;

    prototype.set<Hotbar>();

    auto invRes =
      conn(sqlpp::select(sqlpp::all_of(inventoryTable)).from(inventoryTable).where(inventoryTable.charId == charId));
    auto& inventory = prototype.set<Inventory>();
    for (const auto& row : invRes) {
        if (row.slot >= RoseCommon::MAX_ITEMS) {
            continue;
        }
        Item item;
        item.isCreated = false;
        item.life = 1000;
        item.hasSocket = row.socket;
        item.isAppraised = true;
        item.refine = row.refine;
        item.count = row.amount;
        item.gemOpt = row.gemOpt;
        item.price = 0;
        inventory.items[row.slot] = load_item(row.itemtype, row.itemid, item);
    }

    auto& level = prototype.set<Level>();
    level.xp = charRow.exp;
    level.level = charRow.level;
    level.penaltyXp = charRow.penaltyExp;

    auto& life = prototype.set<Life>();
    life.hp = charRow.maxHp / 3; // you only get 30% of your health when login in
    life.maxHp = charRow.maxHp;

    auto& magic = prototype.set<Magic>();
    magic.mp = charRow.maxMp / 3;
    magic.maxMp = charRow.maxMp;

    auto& pos = prototype.set<Position>();
    pos.x = charRow.x;
    pos.y = charRow.y;
    pos.z = 0;
    pos.spawn = charRow.reviveMap;
	pos.map = charRow.map;

    auto skillRes =
      conn(sqlpp::select(skillsTable.id, skillsTable.level).from(skillsTable).where(skillsTable.charId == charId));
    auto& skills = prototype.set<Skills>();
    for (const auto& [i, row] : Core::enumerate(skillRes)) {
        skills.skills[i].set_id(row.id);
        skills.skills[i].set_level(row.level);
    }

    auto& stamina = prototype.set<Stamina>();
    stamina.stamina = charRow.stamina;

    auto& stats = prototype.set<Stats>();
    stats.str = charRow.str;
    stats.dex = charRow.dex;
    stats.int_ = charRow.int_;
    stats.con = charRow.con;
    stats.charm = charRow.charm;
    stats.sense = charRow.sense;
    stats.bodySize = 100;
    stats.headSize = 100;

    prototype.set<StatusEffects>();

    auto wishRes = conn(sqlpp::select(sqlpp::all_of(wish)).from(wish).where(wish.charId == charId));
    auto& wishlist = prototype.set<Wishlist>();
    for (const auto& row : wishRes) {
        if (row.slot >= RoseCommon::MAX_WISHLIST) {
            continue;
        }
        Item item;
        item.isCreated = false;
        item.life = 1000;
        item.hasSocket = row.socket;
        item.isAppraised = true;
        item.refine = row.refine;
        item.count = row.amount;
        item.gemOpt = row.gemOpt;
        item.price = row.price;
        wishlist.items[row.slot] = load_item(row.itemtype, row.itemid, item);
    }

    std::lock_guard<std::mutex> lock(access);
    return prototype();
}

void EntitySystem::save_character(RoseCommon::Entity character) const {
    //TODO: should be done as a task
}

RoseCommon::Entity EntitySystem::create_item(uint8_t type, uint16_t id) {
    using namespace Component;
    entt::prototype prototype(registry);

    const auto &itemDb = RoseCommon::ItemDatabase::getInstance();
    if (!itemDb.itemExists(type, id)) {
        return entt::null;
    }
    const auto& def = itemDb.getItemDef(type, id);

    auto& item = prototype.set<Item>();
    item.isCreated = false;
    item.life = 1000;
    item.durability = 100;
    item.hasSocket = false;
    item.isAppraised = false;
    item.refine = 0;
    item.count = 0;
    item.gemOpt = 0;
    item.price = 0;

    prototype.set<RoseCommon::ItemDef>(def);

    std::lock_guard<std::mutex> lock(access);
    return prototype();
}

RoseCommon::Entity EntitySystem::load_item(uint8_t type, uint16_t id, Component::Item item) {
    auto entity = create_item(type, id);
    if (entity == entt::null) {
        return entt::null;
    }
    registry.replace<Component::Item>(entity, item);
    return entity;
}

void EntitySystem::save_item(RoseCommon::Entity item, RoseCommon::Entity owner) const {
    // TODO: should be done as a task???
}
<commit_msg>Update entity_system.cpp<commit_after>#include "entity_system.h"
#include "cmapclient.h"
#include "enumerate.h"
#include "itemdb.h"
#include <entt.hpp>
#include "components/basic_info.h"
#include "components/client.h"
#include "components/computed_values.h"
#include "components/faction.h"
#include "components/character_graphics.h"
#include "components/guild.h"
#include "components/hotbar.h"
#include "components/inventory.h"
#include "components/item.h"
#include "components/level.h"
#include "components/life.h"
#include "components/magic.h"
#include "components/npc.h"
#include "components/owner.h"
#include "components/position.h"
#include "components/skills.h"
#include "components/stamina.h"
#include "components/stats.h"
#include "components/status_effects.h"
#include "components/wishlist.h"

#include "chat/normal_chat.h"

EntitySystem::EntitySystem(std::chrono::milliseconds maxTimePerUpdate) : maxTimePerUpdate(maxTimePerUpdate), nearby(*this) {
    logger = Core::CLog::GetLogger(Core::log_type::GENERAL).lock();

    // callback for nearby calculations
    registry.construction<Component::Position>().connect<&Nearby::add_entity>(&nearby);
    registry.destruction<Component::Position>().connect<&Nearby::remove_entity>(&nearby);
    // dispatcher registration
    register_dispatcher(std::function{Chat::normal_chat});
}

void EntitySystem::stop() {
    work_queue.kill();
    registry.construction<Component::Position>().disconnect<&Nearby::add_entity>(&nearby);
    registry.destruction<Component::Position>().disconnect<&Nearby::remove_entity>(&nearby);
}

bool EntitySystem::dispatch_packet(RoseCommon::Entity entity, std::unique_ptr<RoseCommon::CRosePacket>&& packet) {
    if (!packet) {
        return false;
    }
    if (!dispatcher.is_supported(*packet.get())) {
        return false;
    }
    add_task(std::move([this, entity, packet = std::move(packet)](EntitySystem& entitySystem) mutable {
        dispatcher.dispatch(entitySystem, entity, std::move(packet));
    }));
    return true;
}

void EntitySystem::run() {
    for (auto [res, task] = work_queue.pop_front(); res;) {
        {
            std::lock_guard<std::mutex> lock(access);
            std::invoke(std::move(task), *this);
        }
        auto [tmp_res, tmp_task] = work_queue.pop_front();
        res = tmp_res;
        task = std::move(tmp_task);
    }
}

void EntitySystem::send_map(const RoseCommon::CRosePacket& packet) {
    registry.view<Component::Client>().each([](auto entity, auto &client_ptr) {
        (void)entity;
        if (auto client = client_ptr.client.lock()) {
            client->send(packet);
        }
    });
}

void EntitySystem::send_nearby(RoseCommon::Entity entity, const RoseCommon::CRosePacket& packet) {
    registry.view<Component::Client>().each([entity, this](auto other, auto &client_ptr) {
        if (!nearby.is_nearby(entity, other)) return;
        if (auto client = client_ptr.client.lock()) {
            client->send(packet);
        }
    });
}

void EntitySystem::sent_to(RoseCommon::Entity entity, const RoseCommon::CRosePacket& packet) {
    if (auto client_ptr = registry.try_get<Component::Client>(entity)) {
        if (auto client = client_ptr->client.lock()) {
            client->send(packet);
        }
    }
}

void EntitySystem::delete_entity(RoseCommon::Entity entity) {
    add_task([entity](EntitySystem& entitySystem) {
        entitySystem.registry.destroy(entity);
    });
}

RoseCommon::Entity EntitySystem::load_character(uint32_t charId, bool platinium, uint32_t sessionId, std::weak_ptr<CMapClient> client) {
    using namespace Component;
    auto conn = Core::connectionPool.getConnection(Core::osirose);
    Core::CharacterTable characters{};
    Core::InventoryTable inventoryTable{};
    Core::SkillTable skillsTable{};
    Core::WishTable wish{};

    auto charRes = conn(sqlpp::select(sqlpp::count(characters.id), sqlpp::all_of(characters))
                          .from(characters).where(characters.id == charId));

    if (static_cast<long>(charRes.front().count) != 1L) {
        return entt::null;
    }
    const auto& charRow = charRes.front();

    entt::prototype prototype(registry);

    auto& basicInfo = prototype.set<BasicInfo>();
    basicInfo.name = charRow.name;
    basicInfo.id = idManager.get_free_id();
    basicInfo.tag = sessionId;
    basicInfo.teamId = basicInfo.id;
    basicInfo.job = charRow.job;
    basicInfo.statPoints = charRow.statPoints;
    basicInfo.skillPoints = charRow.skillPoints;
    basicInfo.pkFlag = charRow.pkFlag;
    basicInfo.stone = charRow.stone;

    auto& component_client = prototype.set<Client>();
    component_client.client = client;

    auto& computedValues = prototype.set<ComputedValues>();
    computedValues.command = RoseCommon::Command::STOP;
    computedValues.isOnMap.store(false);
    computedValues.moveMode = RoseCommon::MoveMode::WALK;
    computedValues.runSpeed = 0;
    computedValues.atkSpeed = 0;
    computedValues.weightRate = 0;

    auto& faction = prototype.set<Faction>();
    faction.id = charRow.factionid;
    faction.rank = charRow.factionRank;
    faction.fame = charRow.fame;
    faction.factionFame[0] = charRow.factionFame1;
    faction.factionFame[1] = charRow.factionFame2;
    faction.points[0] = charRow.factionPoints1;
    faction.points[1] = charRow.factionPoints2;
    faction.points[2] = charRow.factionPoints3;

    auto& characterGraphics = prototype.set<CharacterGraphics>();
    characterGraphics.face = charRow.face;
    characterGraphics.hair = charRow.hair;
    characterGraphics.race = charRow.race;

    auto& guild = prototype.set<Guild>();
    guild.id = charRow.clanid;
    guild.contribution = charRow.clanContribution;
    guild.rank = charRow.clanRank;

    prototype.set<Hotbar>();

    auto invRes =
      conn(sqlpp::select(sqlpp::all_of(inventoryTable)).from(inventoryTable).where(inventoryTable.charId == charId));
    auto& inventory = prototype.set<Inventory>();
    for (const auto& row : invRes) {
        if (row.slot >= RoseCommon::MAX_ITEMS) {
            continue;
        }
        Item item;
        item.isCreated = false;
        item.life = 1000;
        item.hasSocket = row.socket;
        item.isAppraised = true;
        item.refine = row.refine;
        item.count = row.amount;
        item.gemOpt = row.gemOpt;
        item.price = 0;
        inventory.items[row.slot] = load_item(row.itemtype, row.itemid, item);
    }

    auto& level = prototype.set<Level>();
    level.xp = charRow.exp;
    level.level = charRow.level;
    level.penaltyXp = charRow.penaltyExp;

    auto& life = prototype.set<Life>();
    life.hp = charRow.maxHp / 3; // you only get 30% of your health when login in
    life.maxHp = charRow.maxHp;

    auto& magic = prototype.set<Magic>();
    magic.mp = charRow.maxMp / 3;
    magic.maxMp = charRow.maxMp;

    auto& pos = prototype.set<Position>();
    pos.x = charRow.x;
    pos.y = charRow.y;
    pos.z = 0;
    pos.spawn = charRow.reviveMap;
	pos.map = charRow.map;

    auto skillRes =
      conn(sqlpp::select(skillsTable.id, skillsTable.level).from(skillsTable).where(skillsTable.charId == charId));
    auto& skills = prototype.set<Skills>();
    for (const auto& [i, row] : Core::enumerate(skillRes)) {
        skills.skills[i].set_id(row.id);
        skills.skills[i].set_level(row.level);
    }

    auto& stamina = prototype.set<Stamina>();
    stamina.stamina = charRow.stamina;

    auto& stats = prototype.set<Stats>();
    stats.str = charRow.str;
    stats.dex = charRow.dex;
    stats.int_ = charRow.int_;
    stats.con = charRow.con;
    stats.charm = charRow.charm;
    stats.sense = charRow.sense;
    stats.bodySize = 100;
    stats.headSize = 100;

    prototype.set<StatusEffects>();

    auto wishRes = conn(sqlpp::select(sqlpp::all_of(wish)).from(wish).where(wish.charId == charId));
    auto& wishlist = prototype.set<Wishlist>();
    for (const auto& row : wishRes) {
        if (row.slot >= RoseCommon::MAX_WISHLIST) {
            continue;
        }
        Item item;
        item.isCreated = false;
        item.life = 1000;
        item.hasSocket = row.socket;
        item.isAppraised = true;
        item.refine = row.refine;
        item.count = row.amount;
        item.gemOpt = row.gemOpt;
        item.price = row.price;
        wishlist.items[row.slot] = load_item(row.itemtype, row.itemid, item);
    }

    std::lock_guard<std::mutex> lock(access);
    return prototype();
}

void EntitySystem::save_character(RoseCommon::Entity character) const {
    //TODO: should be done as a task
}

RoseCommon::Entity EntitySystem::create_item(uint8_t type, uint16_t id) {
    using namespace Component;
    entt::prototype prototype(registry);

    const auto &itemDb = RoseCommon::ItemDatabase::getInstance();
    if (!itemDb.itemExists(type, id)) {
        return entt::null;
    }
    const auto& def = itemDb.getItemDef(type, id);

    auto& item = prototype.set<Item>();
    item.isCreated = false;
    item.life = 1000;
    item.durability = 100;
    item.hasSocket = false;
    item.isAppraised = false;
    item.refine = 0;
    item.count = 0;
    item.gemOpt = 0;
    item.price = 0;

    prototype.set<RoseCommon::ItemDef>(def);

    std::lock_guard<std::mutex> lock(access);
    return prototype();
}

RoseCommon::Entity EntitySystem::load_item(uint8_t type, uint16_t id, Component::Item item) {
    auto entity = create_item(type, id);
    if (entity == entt::null) {
        return entt::null;
    }
    registry.replace<Component::Item>(entity, item);
    return entity;
}

void EntitySystem::save_item(RoseCommon::Entity item, RoseCommon::Entity owner) const {
    // TODO: should be done as a task???
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

/*!
 * \file
 * \brief Contains traits for wrapper expressions
*/

#pragma once

namespace etl {

/*!
 * \brief Traits for wrapper expressions
 */
template <typename T>
struct wrapper_traits {
    using expr_t     = T;
    using sub_expr_t = std::decay_t<typename T::expr_t>;
    using sub_traits = etl_traits<sub_expr_t>;

    static constexpr const bool is_etl                  = sub_traits::is_etl;                  ///< Indicates if the type is an ETL expression
    static constexpr const bool is_transformer          = sub_traits::is_transformer;          ///< Indicates if the type is a transformer
    static constexpr const bool is_view                 = sub_traits::is_view;                 ///< Indicates if the type is a view
    static constexpr const bool is_magic_view           = sub_traits::is_magic_view;           ///< Indicates if the type is a magic view
    static constexpr const bool is_fast                 = sub_traits::is_fast;                 ///< Indicates if the expression is fast
    static constexpr const bool is_value                = sub_traits::is_value;                ///< Indicates if the expression is of value type
    static constexpr const bool is_direct                = sub_traits::is_direct;                ///< Indicates if the expression has direct memory access
    static constexpr const bool is_linear               = sub_traits::is_linear;               ///< Indicates if the expression is linear
    static constexpr const bool is_generator            = sub_traits::is_generator;            ///< Indicates if the expression is a generator expression
    static constexpr const bool needs_temporary_visitor = sub_traits::needs_temporary_visitor; ///< Indicates if the expression needs a temporary visitor
    static constexpr const bool needs_evaluator_visitor = sub_traits::needs_evaluator_visitor; ///< Indicaes if the expression needs an evaluator visitor
    static constexpr const order storage_order          = sub_traits::storage_order;           ///< The expression storage order

    /*!
     * The vectorization type for V
     */
    template <vector_mode_t V>
    using vectorizable = typename sub_traits::template vectorizable<V>;

    /*!
     * \brief Returns the size of the given expression
     * \param v The expression to get the size for
     * \returns the size of the given expression
     */
    static std::size_t size(const expr_t& v) {
        return sub_traits::size(v.value());
    }

    /*!
     * \brief Returns the dth dimension of the given expression
     * \param v The expression
     * \param d The dimension to get
     * \return The dth dimension of the given expression
     */
    static std::size_t dim(const expr_t& v, std::size_t d) {
        return sub_traits::dim(v.value(), d);
    }

    /*!
     * \brief Returns the size of an expression of this fast type.
     * \returns the size of an expression of this fast type.
     */
    static constexpr std::size_t size() {
        return sub_traits::size();
    }

    /*!
     * \brief Returns the Dth dimension of an expression of this type
     * \tparam D The dimension to get
     * \return the Dth dimension of an expression of this type
     */
    template <std::size_t D>
    static constexpr std::size_t dim() {
        return sub_traits::template dim<D>();
    }

    /*!
     * \brief Returns the number of expressions for this type
     * \return the number of dimensions of this type
     */
    static constexpr std::size_t dimensions() {
        return sub_traits::dimensions();
    }
};

} //end of namespace etl
<commit_msg>Add is_thread_safe to wrapper traits<commit_after>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

/*!
 * \file
 * \brief Contains traits for wrapper expressions
*/

#pragma once

namespace etl {

/*!
 * \brief Traits for wrapper expressions
 */
template <typename T>
struct wrapper_traits {
    using expr_t     = T;
    using sub_expr_t = std::decay_t<typename T::expr_t>;
    using sub_traits = etl_traits<sub_expr_t>;

    static constexpr const bool is_etl                  = sub_traits::is_etl;                  ///< Indicates if the type is an ETL expression
    static constexpr const bool is_transformer          = sub_traits::is_transformer;          ///< Indicates if the type is a transformer
    static constexpr const bool is_view                 = sub_traits::is_view;                 ///< Indicates if the type is a view
    static constexpr const bool is_magic_view           = sub_traits::is_magic_view;           ///< Indicates if the type is a magic view
    static constexpr const bool is_fast                 = sub_traits::is_fast;                 ///< Indicates if the expression is fast
    static constexpr const bool is_value                = sub_traits::is_value;                ///< Indicates if the expression is of value type
    static constexpr const bool is_direct               = sub_traits::is_direct;               ///< Indicates if the expression has direct memory access
    static constexpr const bool is_linear               = sub_traits::is_linear;               ///< Indicates if the expression is linear
    static constexpr const bool is_thread_safe          = sub_traits::is_thread_safe;          ///< Indicates if the expression is thread safe
    static constexpr const bool is_generator            = sub_traits::is_generator;            ///< Indicates if the expression is a generator expression
    static constexpr const bool needs_temporary_visitor = sub_traits::needs_temporary_visitor; ///< Indicates if the expression needs a temporary visitor
    static constexpr const bool needs_evaluator_visitor = sub_traits::needs_evaluator_visitor; ///< Indicaes if the expression needs an evaluator visitor
    static constexpr const order storage_order          = sub_traits::storage_order;           ///< The expression storage order

    /*!
     * The vectorization type for V
     */
    template <vector_mode_t V>
    using vectorizable = typename sub_traits::template vectorizable<V>;

    /*!
     * \brief Returns the size of the given expression
     * \param v The expression to get the size for
     * \returns the size of the given expression
     */
    static std::size_t size(const expr_t& v) {
        return sub_traits::size(v.value());
    }

    /*!
     * \brief Returns the dth dimension of the given expression
     * \param v The expression
     * \param d The dimension to get
     * \return The dth dimension of the given expression
     */
    static std::size_t dim(const expr_t& v, std::size_t d) {
        return sub_traits::dim(v.value(), d);
    }

    /*!
     * \brief Returns the size of an expression of this fast type.
     * \returns the size of an expression of this fast type.
     */
    static constexpr std::size_t size() {
        return sub_traits::size();
    }

    /*!
     * \brief Returns the Dth dimension of an expression of this type
     * \tparam D The dimension to get
     * \return the Dth dimension of an expression of this type
     */
    template <std::size_t D>
    static constexpr std::size_t dim() {
        return sub_traits::template dim<D>();
    }

    /*!
     * \brief Returns the number of expressions for this type
     * \return the number of dimensions of this type
     */
    static constexpr std::size_t dimensions() {
        return sub_traits::dimensions();
    }
};

} //end of namespace etl
<|endoftext|>


<commit_before>#include "script_loader.h"

using namespace LuaScript;

ScriptLoader::File::File(std::string const& path) : path_(path) {
    //TODO: find filename
    //prob split on '/' and take latest, then split on '.' and take everything but the last one if there's more than one
}

ScriptLoader::ScriptLoader(std::shared_ptr<EntitySystem> entity_system, uint16_t map_id, std::string const& path):
entity_system_(entity_system),
logger_(Core::CLog::GetLogger(Core::log_type::SCRIPTLOADER).lock()),
path_(path), map_id_(map_id) {
    state_.open_libraries(); //FIXME: check if we need all libs

    state_.set_function("include", [this](std::string path) {
        load_script(path);
    });
}

void ScriptLoader::load_script() {
    reload_scripts(path_);
}

void ScriptLoader::load_npcs() {
    for (auto& [file, entities] : npc_files_) {
        entity_system_->bulk_destroy(entities);
        entities.clear();
        load_script(file.path_);
    }
}

void ScriptLoader::load_warpgates() {
    for (auto& [file, entities] : warpgate_files_) {
        entity_system_->bulk_destroy(entities);
        entities.clear();
        load_script(file.path_);
    }
}

void ScriptLoader::load_spawners() {
    for (auto& [file, entities] : spawner_files_) {
        entity_system_->bulk_destroy(entities);
        entities.clear();
        load_script(file.path_);
    }
}

void ScriptLoader::load_script(std::string const& path) {
    try {
        File file = File{path};
        sol::environment env{state_, sol::create, state_.globals()};

        auto warpgate_file = warpgate_files_.find(files);
        std::vector<Entity> warpgates;
        if (warpgate_file != warpgate_files_.end()) {
            warpgates = std::move(warpgate_file->second);
        }
        env.set_function("warp_gate", [warpgates](std::string alias, int dest_map_id, float dest_x, float dest_y, float dest_z, int map_id, float x, float y, float z, float angle, float x_scale, float y_scale, float z_scale) {
            warpgates.push_back(entity_system_->create_warpgate(alias, dest_map_id, dest_x, dest_y, dest_z, map_id, x, y, z, angle, x_scale, y_scale, z_scale));
        });

        auto npc_file = npc_files_.find(file);
        std::vector<Entity> npcs;
        if (npc_file != npc_files_.end()) {
            npcs = std::move(npc_file->second);
        }
        env.set_function("npc", [npc_file](std::string npc_lua, int npc_id, int map_id, float x, float y, float z, float angle) {
            npc_file->second.push_back(entity_system_->create_npc(npc_lua, npc_id, map_id, x, y, z, angle));
        });

        auto spawner_file = spawner_files_.find(file);
        std::vector<Entity> spawners;
        if (spawner_file != spawner_files_.end()) {
            spawners = std::move(spawners_file->second);
        }
        env.set_function("mob", [spawners](std::string alias, int mob_id, int mob_count, int limit, int interval, int range, int map_id, float x, float y, float z) {
            spawners.push_back(entity_system_->create_spawner(alias, mob_id, mob_count, limit, interval, range, map_id, x, y, z));
        });

        state_.script(path, env);
        logger_->info("Finished (re)loading scripts from '{}'", path);

        if (warpgates.size()) warpgate_files_.insert_or_assign(std::make_pair(file, std::move(warpgates)));
        if (npcs.size()) npc_files_.insert_or_assign(std::make_pair(file, std::move(npcs)));
        if (spawners.size()) spawner_files_.insert_or_assign(std::make_pair(file, std::move(spawners)));
    } catch (const sol::error& e) {
        logger_->error("Error (re)loading lua scripts '{}' : {}", path, e.what());
    }
}
<commit_msg>Update script_loader.cpp<commit_after>#include "script_loader.h"

using namespace LuaScript;

ScriptLoader::File::File(std::string const& path) : path_(path) {
    //TODO: find filename
    //prob split on '/' and take latest, then split on '.' and take everything but the last one if there's more than one
}

ScriptLoader::ScriptLoader(std::shared_ptr<EntitySystem> entity_system, uint16_t map_id, std::string const& path):
entity_system_(entity_system),
logger_(Core::CLog::GetLogger(Core::log_type::SCRIPTLOADER).lock()),
path_(path), map_id_(map_id) {
    state_.open_libraries(); //FIXME: check if we need all libs

    state_.set_function("include", [this](std::string path) {
        load_script(path);
    });
}

void ScriptLoader::load_script() {
    load_script(path_);
}

void ScriptLoader::load_npcs() {
    for (auto& [file, entities] : npc_files_) {
        entity_system_->bulk_destroy(entities);
        entities.clear();
        load_script(file.path_);
    }
}

void ScriptLoader::load_warpgates() {
    for (auto& [file, entities] : warpgate_files_) {
        entity_system_->bulk_destroy(entities);
        entities.clear();
        load_script(file.path_);
    }
}

void ScriptLoader::load_spawners() {
    for (auto& [file, entities] : spawner_files_) {
        entity_system_->bulk_destroy(entities);
        entities.clear();
        load_script(file.path_);
    }
}

void ScriptLoader::load_script(std::string const& path) {
    try {
        File file = File{path};
        sol::environment env{state_, sol::create, state_.globals()};

        auto warpgate_file = warpgate_files_.find(file);
        std::vector<Entity> warpgates;
        if (warpgate_file != warpgate_files_.end()) {
            warpgates = std::move(warpgate_file->second);
        }
        env.set_function("warp_gate", [warpgates](std::string alias, int dest_map_id, float dest_x, float dest_y, float dest_z, int map_id, float x, float y, float z, float angle, float x_scale, float y_scale, float z_scale) {
            warpgates.push_back(entity_system_->create_warpgate(alias, dest_map_id, dest_x, dest_y, dest_z, map_id, x, y, z, angle, x_scale, y_scale, z_scale));
        });

        auto npc_file = npc_files_.find(file);
        std::vector<Entity> npcs;
        if (npc_file != npc_files_.end()) {
            npcs = std::move(npc_file->second);
        }
        env.set_function("npc", [npc_file](std::string npc_lua, int npc_id, int map_id, float x, float y, float z, float angle) {
            npc_file->second.push_back(entity_system_->create_npc(npc_lua, npc_id, map_id, x, y, z, angle));
        });

        auto spawner_file = spawner_files_.find(file);
        std::vector<Entity> spawners;
        if (spawner_file != spawner_files_.end()) {
            spawners = std::move(spawners_file->second);
        }
        env.set_function("mob", [spawners](std::string alias, int mob_id, int mob_count, int limit, int interval, int range, int map_id, float x, float y, float z) {
            spawners.push_back(entity_system_->create_spawner(alias, mob_id, mob_count, limit, interval, range, map_id, x, y, z));
        });

        state_.script(path, env);
        logger_->info("Finished (re)loading scripts from '{}'", path);

        if (warpgates.size()) warpgate_files_.insert_or_assign(std::make_pair(file, std::move(warpgates)));
        if (npcs.size()) npc_files_.insert_or_assign(std::make_pair(file, std::move(npcs)));
        if (spawners.size()) spawner_files_.insert_or_assign(std::make_pair(file, std::move(spawners)));
    } catch (const sol::error& e) {
        logger_->error("Error (re)loading lua scripts '{}' : {}", path, e.what());
    }
}
<|endoftext|>


<commit_before>//
//  mutex.hpp
//  fibio
//
//  Created by Chen Xu on 14-3-4.
//  Copyright (c) 2014 0d0a.com. All rights reserved.
//

#ifndef fibio_mutex_hpp
#define fibio_mutex_hpp

#include <memory>
#include <chrono>
#include <mutex>
#include <fibio/fibers/detail/forward.hpp>

namespace fibio { namespace fibers {
    class mutex {
    public:
        /// constructor
        mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * unlocks the mutex
         */
        void unlock();
    private:
        /// non-copyable
        mutex(const mutex&) = delete;
        void operator=(const mutex&) = delete;
        std::shared_ptr<detail::mutex_object> impl_;
        friend class condition_variable;
    };

    class timed_mutex {
    public:
        /// constructor
        timed_mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable for the specified timeout duration
         */
        template<class Rep, class Period>
        bool try_lock_for(const std::chrono::duration<Rep,Period>& timeout_duration ) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_duration).count());
        }

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable until specified time point has been reached
         */
        template< class Clock, class Duration >
        bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time ) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_time - std::chrono::steady_clock::now()).count());
        }

        /**
         * unlocks the mutex
         */
        void unlock();
    private:
        /// non-copyable
        timed_mutex(const timed_mutex&) = delete;
        void operator=(const timed_mutex&) = delete;
        bool try_lock_usec(uint64_t usec);
        std::shared_ptr<detail::timed_mutex_object> impl_;
    };

    class recursive_mutex {
    public:
        /// constructor
        recursive_mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * unlocks the mutex
         */
        void unlock();

    private:
        /// non-copyable
        recursive_mutex(const recursive_mutex&) = delete;
        void operator=(const recursive_mutex&) = delete;
        std::shared_ptr<detail::recursive_mutex_object> impl_;
    };

    class recursive_timed_mutex {
    public:
        /// constructor
        recursive_timed_mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * unlocks the mutex
         */
        void unlock();

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable for the specified timeout duration
         */
        template<class Rep, class Period>
        bool try_lock_for(const std::chrono::duration<Rep,Period>& timeout_duration) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_duration).count());
        }

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable until specified time point has been reached
         */
        template<class Clock, class Duration>
        bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_time - std::chrono::steady_clock::now()).count());
        }

    private:
        /// non-copyable
        recursive_timed_mutex(const recursive_timed_mutex&) = delete;
        void operator=(const recursive_timed_mutex&) = delete;
        bool try_lock_usec(uint64_t usec);
        std::shared_ptr<detail::recursive_timed_mutex_object> impl_;
    };
}}  // End of namespace fibio::fibers

namespace fibio {
    using fibers::mutex;
    using fibers::timed_mutex;
    using fibers::recursive_mutex;
    using fibers::recursive_timed_mutex;

    using std::lock_guard;
    using std::unique_lock;
    using std::defer_lock_t;
    using std::try_to_lock_t;
    using std::adopt_lock_t;
    using std::defer_lock;
    using std::try_to_lock;
    using std::adopt_lock;
    using std::try_lock;
    using std::lock;
}   // End of namespace fibio

#endif
<commit_msg>warning<commit_after>//
//  mutex.hpp
//  fibio
//
//  Created by Chen Xu on 14-3-4.
//  Copyright (c) 2014 0d0a.com. All rights reserved.
//

#ifndef fibio_mutex_hpp
#define fibio_mutex_hpp

#include <memory>
#include <chrono>
#include <mutex>
#include <fibio/fibers/detail/forward.hpp>

namespace fibio { namespace fibers {
    class mutex {
    public:
        /// constructor
        mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * unlocks the mutex
         */
        void unlock();
    private:
        /// non-copyable
        mutex(const mutex&) = delete;
        void operator=(const mutex&) = delete;
        std::shared_ptr<detail::mutex_object> impl_;
        friend struct condition_variable;
    };

    class timed_mutex {
    public:
        /// constructor
        timed_mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable for the specified timeout duration
         */
        template<class Rep, class Period>
        bool try_lock_for(const std::chrono::duration<Rep,Period>& timeout_duration ) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_duration).count());
        }

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable until specified time point has been reached
         */
        template< class Clock, class Duration >
        bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time ) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_time - std::chrono::steady_clock::now()).count());
        }

        /**
         * unlocks the mutex
         */
        void unlock();
    private:
        /// non-copyable
        timed_mutex(const timed_mutex&) = delete;
        void operator=(const timed_mutex&) = delete;
        bool try_lock_usec(uint64_t usec);
        std::shared_ptr<detail::timed_mutex_object> impl_;
    };

    class recursive_mutex {
    public:
        /// constructor
        recursive_mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * unlocks the mutex
         */
        void unlock();

    private:
        /// non-copyable
        recursive_mutex(const recursive_mutex&) = delete;
        void operator=(const recursive_mutex&) = delete;
        std::shared_ptr<detail::recursive_mutex_object> impl_;
    };

    class recursive_timed_mutex {
    public:
        /// constructor
        recursive_timed_mutex();

        /**
         * locks the mutex, blocks if the mutex is not available
         */
        void lock();

        /**
         * tries to lock the mutex, returns if the mutex is not available
         */
        bool try_lock();

        /**
         * unlocks the mutex
         */
        void unlock();

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable for the specified timeout duration
         */
        template<class Rep, class Period>
        bool try_lock_for(const std::chrono::duration<Rep,Period>& timeout_duration) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_duration).count());
        }

        /**
         * tries to lock the mutex, returns if the mutex has been
         * unavailable until specified time point has been reached
         */
        template<class Clock, class Duration>
        bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time) {
            return try_lock_usec(std::chrono::duration_cast<std::chrono::microseconds>(timeout_time - std::chrono::steady_clock::now()).count());
        }

    private:
        /// non-copyable
        recursive_timed_mutex(const recursive_timed_mutex&) = delete;
        void operator=(const recursive_timed_mutex&) = delete;
        bool try_lock_usec(uint64_t usec);
        std::shared_ptr<detail::recursive_timed_mutex_object> impl_;
    };
}}  // End of namespace fibio::fibers

namespace fibio {
    using fibers::mutex;
    using fibers::timed_mutex;
    using fibers::recursive_mutex;
    using fibers::recursive_timed_mutex;

    using std::lock_guard;
    using std::unique_lock;
    using std::defer_lock_t;
    using std::try_to_lock_t;
    using std::adopt_lock_t;
    using std::defer_lock;
    using std::try_to_lock;
    using std::adopt_lock;
    using std::try_lock;
    using std::lock;
}   // End of namespace fibio

#endif
<|endoftext|>


<commit_before>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2017 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <iostream>
#include <map>
#include <memory>
#include <stdexcept>
#include <vector>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <libgen.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <kchashdb.h>
#include "Downloader.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcUtil.h"
#include "MarcWriter.h"
#include "RegexMatcher.h"
#include "Semaphore.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "TimeLimit.h"
#include "util.h"

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname
              << "[--max-record-count count] [--skip-count count] [--process-count-low-and-high-watermarks low:high] "
              << "marc_input marc_output full_text_db\n"
              << "       --process-count-low-and-high-watermarks sets the maximum and minimum number of spawned\n"
              << "       child processes.  When we hit the high water mark we wait for child processes to exit\n"
              << "       until we reach the low watermark.\n\n";

    std::exit(EXIT_FAILURE);
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const Subfields &subfields) {
    const auto &_3_begin_end(subfields.getIterators('3'));
    if (_3_begin_end.first != _3_begin_end.second) {
        if (StringUtil::StartsWith(_3_begin_end.first->value_, "Rezension"))
            return true;
    } else {
        const auto &z_begin_end(subfields.getIterators('z'));
        if (z_begin_end.first != z_begin_end.second
            and StringUtil::StartsWith(z_begin_end.first->value_, "Rezension"))
            return true;
    }

    return false;
}

bool FoundAtLeastOneNonReviewLink(const MarcRecord &record) {
    for (size_t _856_index(record.getFieldIndex("856")); record.getTag(_856_index) == "856"; ++_856_index) {
        const Subfields subfields(record.getSubfields(_856_index));
        if (subfields.getIndicator1() == '7' or not subfields.hasSubfield('u'))
            continue;

        if (not IsProbablyAReview(subfields))
            return true;
    }

    return false;
}

// Returns the number of child processes that returned a non-zero exit code.
unsigned CleanUpZombies(const unsigned zombies_to_collect) {
    unsigned child_reported_failure_count(0);
    for (unsigned zombie_no(0); zombie_no < zombies_to_collect; ++zombie_no) {
        int exit_code;
        ::wait(&exit_code);
        if (exit_code != 0)
            ++child_reported_failure_count;
    }

    return child_reported_failure_count;
}

void ProcessRecords(const unsigned max_record_count, const unsigned skip_count, MarcReader * const marc_reader,
                    MarcWriter * const marc_writer, const std::string &db_filename,
                    const unsigned process_count_low_watermark, const unsigned process_count_high_watermark)
{
    Semaphore semaphore("/full_text_cached_counter", Semaphore::CREATE);
    std::string err_msg;
    unsigned total_record_count(0), spawn_count(0), active_child_count(0), child_reported_failure_count(0);

    const std::string UPDATE_FULL_TEXT_DB_PATH("/usr/local/bin/update_full_text_db");

    std::cout << "Skip " << skip_count << " records\n";
    while (MarcRecord record = marc_reader->read()) {
        if (total_record_count == max_record_count)
            break;
        ++total_record_count;
        if (total_record_count <= skip_count)
             continue;

        const bool insert_in_cache(FoundAtLeastOneNonReviewLink(record)
                                   or (not MarcUtil::HasTagAndSubfield(record, "856", 'u')
                                       and MarcUtil::HasTagAndSubfield(record, "520", 'a')));
        if (not insert_in_cache) {
            MarcUtil::FileLockedComposeAndWriteRecord(marc_writer, &record);
            continue;
        }

        ExecUtil::Spawn(UPDATE_FULL_TEXT_DB_PATH,
                        { std::to_string(marc_reader->tell()), marc_reader->getPath(),
                          marc_writer->getFile().getPath(), db_filename });
        ++active_child_count;
        ++spawn_count;

        if (active_child_count > process_count_high_watermark) {
            child_reported_failure_count += CleanUpZombies(active_child_count - process_count_low_watermark);
            active_child_count = process_count_low_watermark;
        }
    }

    // Wait for stragglers:
    child_reported_failure_count += CleanUpZombies(active_child_count);

    if (not err_msg.empty())
        Error(err_msg);
    std::cerr << "Read " << total_record_count << " records.\n";
    std::cerr << "Spawned " << spawn_count << " subprocesses.\n";
    std::cerr << semaphore.getValue()
              << " documents were not downloaded because their cached values had not yet expired.\n";
    std::cerr << child_reported_failure_count << " children reported a failure!\n";
}

constexpr unsigned PROCESS_COUNT_DEFAULT_HIGH_WATERMARK(10);
constexpr unsigned PROCESS_COUNT_DEFAULT_LOW_WATERMARK(5);

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 4 and argc != 6 and argc != 8 and argc != 10)
        Usage();
    ++argv; // skip program name

    // Process optional args:
    unsigned max_record_count(UINT_MAX), skip_count(0);
    unsigned process_count_low_watermark(PROCESS_COUNT_DEFAULT_LOW_WATERMARK),
             process_count_high_watermark(PROCESS_COUNT_DEFAULT_HIGH_WATERMARK);
    while (argc > 4) {
        std::cout << "Arg: " << *argv << "\n";
        if (std::strcmp(*argv, "--max-record-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &max_record_count) or max_record_count == 0)
                Error("bad value for --max-record-count!");
            ++argv;
            argc -= 2;
        } else if (std::strcmp(*argv, "--skip-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &skip_count))
                Error("bad value for --skip-count!");
            std::cout << "Should skip " << skip_count << " records\n";
            ++argv;
            argc -= 2;
        } else if (std::strcmp("--process-count-low-and-high-watermarks", *argv) == 0) {
            ++argv;
            char *arg_end(*argv + std::strlen(*argv));
            char * const colon(std::find(*argv, arg_end, ':'));
            if (colon == arg_end)
                Error("bad argument to --process-count-low-and-high-watermarks: colon is missing!");
            *colon = '\0';
            if (not StringUtil::ToNumber(*argv, &process_count_low_watermark)
                or not StringUtil::ToNumber(*argv, &process_count_high_watermark))
                Error("low or high watermark is not an unsigned number!");
            if (process_count_high_watermark > process_count_low_watermark)
                Error("the high watermark must be larger than the low watermark!");
            ++argv;
            argc -= 2;
        } else
            Error("unknown flag: " + std::string(*argv));
    }

    const std::string marc_input_filename(*argv++);
    const std::string marc_output_filename(*argv++);
    if (marc_input_filename == marc_output_filename)
        Error("input filename must not equal output filename!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    const std::string db_filename(*argv);
    kyotocabinet::HashDB db;
    if (not db.open(db_filename, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OCREATE))
        Error("Failed to create the key/valuedatabase \"" + db_filename + "\" ("
              + std::string(db.error().message()) + ")!");
    db.close();

    const std::string UPDATE_DB_LOG_DIR_PATH(
        "/var/log/" + std::string(FileUtil::Exists("/var/log/krimdok") ? "krimdok" : "ixtheo")
        + "/update_full_text_db");
    if (not FileUtil::MakeDirectory(UPDATE_DB_LOG_DIR_PATH, /* recursive = */ true))
        Error("failed to create directory: " + UPDATE_DB_LOG_DIR_PATH);

    try {
        ProcessRecords(max_record_count, skip_count, marc_reader.get(), marc_writer.get(), db_filename,
                       process_count_low_watermark, process_count_high_watermark);
    } catch (const std::exception &e) {
        Error("Caught exception: " + std::string(e.what()));
    }
}
<commit_msg>Reverted a bad change.<commit_after>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2017 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <iostream>
#include <map>
#include <memory>
#include <stdexcept>
#include <vector>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <libgen.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <kchashdb.h>
#include "Downloader.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcUtil.h"
#include "MarcWriter.h"
#include "RegexMatcher.h"
#include "Semaphore.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "TimeLimit.h"
#include "util.h"

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname
              << "[--max-record-count count] [--skip-count count] [--process-count-low-and-high-watermarks low:high] "
              << "marc_input marc_output full_text_db\n"
              << "       --process-count-low-and-high-watermarks sets the maximum and minimum number of spawned\n"
              << "       child processes.  When we hit the high water mark we wait for child processes to exit\n"
              << "       until we reach the low watermark.\n\n";

    std::exit(EXIT_FAILURE);
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const Subfields &subfields) {
    const auto &_3_begin_end(subfields.getIterators('3'));
    if (_3_begin_end.first != _3_begin_end.second) {
        if (StringUtil::StartsWith(_3_begin_end.first->value_, "Rezension"))
            return true;
    } else {
        const auto &z_begin_end(subfields.getIterators('z'));
        if (z_begin_end.first != z_begin_end.second
            and StringUtil::StartsWith(z_begin_end.first->value_, "Rezension"))
            return true;
    }

    return false;
}

bool FoundAtLeastOneNonReviewLink(const MarcRecord &record) {
    for (size_t _856_index(record.getFieldIndex("856")); record.getTag(_856_index) == "856"; ++_856_index) {
        const Subfields subfields(record.getSubfields(_856_index));
        if (subfields.getIndicator1() == '7' or not subfields.hasSubfield('u'))
            continue;

        if (not IsProbablyAReview(subfields))
            return true;
    }

    return false;
}

// Returns the number of child processes that returned a non-zero exit code.
unsigned CleanUpZombies(const unsigned zombies_to_collect) {
    unsigned child_reported_failure_count(0);
    for (unsigned zombie_no(0); zombie_no < zombies_to_collect; ++zombie_no) {
        int exit_code;
        ::wait(&exit_code);
        if (exit_code != 0)
            ++child_reported_failure_count;
    }

    return child_reported_failure_count;
}

void ProcessRecords(const unsigned max_record_count, const unsigned skip_count, MarcReader * const marc_reader,
                    MarcWriter * const marc_writer, const std::string &db_filename,
                    const unsigned process_count_low_watermark, const unsigned process_count_high_watermark)
{
    Semaphore semaphore("/full_text_cached_counter", Semaphore::CREATE);
    std::string err_msg;
    unsigned total_record_count(0), spawn_count(0), active_child_count(0), child_reported_failure_count(0);

    const std::string UPDATE_FULL_TEXT_DB_PATH("/usr/local/bin/update_full_text_db");

    std::cout << "Skip " << skip_count << " records\n";
    off_t record_start = marc_reader->tell();
    while (MarcRecord record = marc_reader->read()) {
        if (total_record_count == max_record_count)
            break;
        ++total_record_count;
        if (total_record_count <= skip_count) {
            record_start = marc_reader->tell();
            continue;
        }

        const bool insert_in_cache(FoundAtLeastOneNonReviewLink(record)
                                   or (not MarcUtil::HasTagAndSubfield(record, "856", 'u')
                                       and MarcUtil::HasTagAndSubfield(record, "520", 'a')));
        if (not insert_in_cache) {
            MarcUtil::FileLockedComposeAndWriteRecord(marc_writer, &record);
            record_start = marc_reader->tell();
            continue;
        }

        ExecUtil::Spawn(UPDATE_FULL_TEXT_DB_PATH,
                        { std::to_string(record_start), marc_reader->getPath(), marc_writer->getFile().getPath(),
                          db_filename });
        ++active_child_count;
        ++spawn_count;

        if (active_child_count > process_count_high_watermark) {
            child_reported_failure_count += CleanUpZombies(active_child_count - process_count_low_watermark);
            active_child_count = process_count_low_watermark;
        }
        record_start = marc_reader->tell();
    }

    // Wait for stragglers:
    child_reported_failure_count += CleanUpZombies(active_child_count);

    if (not err_msg.empty())
        Error(err_msg);
    std::cerr << "Read " << total_record_count << " records.\n";
    std::cerr << "Spawned " << spawn_count << " subprocesses.\n";
    std::cerr << semaphore.getValue()
              << " documents were not downloaded because their cached values had not yet expired.\n";
    std::cerr << child_reported_failure_count << " children reported a failure!\n";
}

constexpr unsigned PROCESS_COUNT_DEFAULT_HIGH_WATERMARK(10);
constexpr unsigned PROCESS_COUNT_DEFAULT_LOW_WATERMARK(5);

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 4 and argc != 6 and argc != 8 and argc != 10)
        Usage();
    ++argv; // skip program name

    // Process optional args:
    unsigned max_record_count(UINT_MAX), skip_count(0);
    unsigned process_count_low_watermark(PROCESS_COUNT_DEFAULT_LOW_WATERMARK),
             process_count_high_watermark(PROCESS_COUNT_DEFAULT_HIGH_WATERMARK);
    while (argc > 4) {
        std::cout << "Arg: " << *argv << "\n";
        if (std::strcmp(*argv, "--max-record-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &max_record_count) or max_record_count == 0)
                Error("bad value for --max-record-count!");
            ++argv;
            argc -= 2;
        } else if (std::strcmp(*argv, "--skip-count") == 0) {
            ++argv;
            if (not StringUtil::ToNumber(*argv, &skip_count))
                Error("bad value for --skip-count!");
            std::cout << "Should skip " << skip_count << " records\n";
            ++argv;
            argc -= 2;
        } else if (std::strcmp("--process-count-low-and-high-watermarks", *argv) == 0) {
            ++argv;
            char *arg_end(*argv + std::strlen(*argv));
            char * const colon(std::find(*argv, arg_end, ':'));
            if (colon == arg_end)
                Error("bad argument to --process-count-low-and-high-watermarks: colon is missing!");
            *colon = '\0';
            if (not StringUtil::ToNumber(*argv, &process_count_low_watermark)
                or not StringUtil::ToNumber(*argv, &process_count_high_watermark))
                Error("low or high watermark is not an unsigned number!");
            if (process_count_high_watermark > process_count_low_watermark)
                Error("the high watermark must be larger than the low watermark!");
            ++argv;
            argc -= 2;
        } else
            Error("unknown flag: " + std::string(*argv));
    }

    const std::string marc_input_filename(*argv++);
    const std::string marc_output_filename(*argv++);
    if (marc_input_filename == marc_output_filename)
        Error("input filename must not equal output filename!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    const std::string db_filename(*argv);
    kyotocabinet::HashDB db;
    if (not db.open(db_filename, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OCREATE))
        Error("Failed to create the key/valuedatabase \"" + db_filename + "\" ("
              + std::string(db.error().message()) + ")!");
    db.close();

    const std::string UPDATE_DB_LOG_DIR_PATH(
        "/var/log/" + std::string(FileUtil::Exists("/var/log/krimdok") ? "krimdok" : "ixtheo")
        + "/update_full_text_db");
    if (not FileUtil::MakeDirectory(UPDATE_DB_LOG_DIR_PATH, /* recursive = */ true))
        Error("failed to create directory: " + UPDATE_DB_LOG_DIR_PATH);

    try {
        ProcessRecords(max_record_count, skip_count, marc_reader.get(), marc_writer.get(), db_filename,
                       process_count_low_watermark, process_count_high_watermark);
    } catch (const std::exception &e) {
        Error("Caught exception: " + std::string(e.what()));
    }
}
<|endoftext|>


<commit_before>/** \file translation_db_tool.cc
 *  \brief A tool for reading/editing of the "translations" SQL table.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2016 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <set>
#include <cstdlib>
#include <cstring>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "IniFile.h"
#include "SimpleXmlParser.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "TranslationUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " command [args]\n\n";
    std::cerr << "       Possible commands are:\n";
    std::cerr << "       get_missing language_code\n";
    std::cerr << "       insert token language_code text\n";
    std::cerr << "       insert ppn gnd_code language_code text\n";
    std::exit(EXIT_FAILURE);
}

void ExecSqlOrDie(const std::string &select_statement, DbConnection * const connection) {
    if (unlikely(not connection->query(select_statement)))
        Error("SQL Statement failed: " + select_statement + " (" + connection->getLastErrorMessage() + ")");
}

// Replaces a comma with "\," and a slash with "\\".
std::string EscapeCommasAndBackslashes(const std::string &text) {
    std::string escaped_text;
    for (const auto ch : text) {
        if (unlikely(ch == ',' or ch == '\\'))
            escaped_text += '\\';
        escaped_text += ch;
    }

    return escaped_text;
}

unsigned GetMissing(DbConnection * const connection, const std::string &table_name,
                    const std::string &table_key_name, const std::string &category,
                    const std::string &language_code)
{
     // Find a token/ppn where "language_code" is missing:
    ExecSqlOrDie("SELECT distinct " + table_key_name + " FROM " + table_name + " WHERE " + table_key_name +
                 " NOT IN (SELECT distinct " + table_key_name + " FROM " + table_name +
                 " WHERE language_code = \"" + language_code + "\") ORDER BY RAND();", connection);
    DbResultSet keys_result_set(connection->getLastResultSet());
    const size_t count(keys_result_set.size());
    if (count == 0)
        return 0;

    // Print the contents of all rows with the token from the last query on stdout:
    DbRow row(keys_result_set.getNextRow());
    const std::string matching_key(row[table_key_name]);
    ExecSqlOrDie("SELECT * FROM " + table_name + " WHERE " + table_key_name + "='" + matching_key + "';", connection);
    DbResultSet result_set(connection->getLastResultSet());
    if (result_set.empty())
        return 0;

    const std::set<std::string> column_names(SqlUtil::GetColumnNames(connection, table_name));
    const bool has_gnd_code(column_names.find("gnd_code") != column_names.cend());

    while (row = result_set.getNextRow())
        std::cout << row[table_key_name] << ',' << count << ',' << row["language_code"] << ','
                  << EscapeCommasAndBackslashes(row["translation"]) << ',' << category
                  << (has_gnd_code ? "," + row["gnd_code"] : "") << '\n';

    return result_set.size();
}

unsigned GetMissingVuFindTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "vufind_translations", "token", "vufind_translations", language_code);
}

unsigned GetMissingKeywordTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "keyword_translations", "ppn", "keywords", language_code);
}

void InsertIntoVuFindTranslations(DbConnection * const connection, const std::string &token,
                                  const std::string &language_code, const std::string &text)
{
    ExecSqlOrDie("INSERT INTO vufind_translations SET token=\"" + token + "\",language_code=\"" + language_code
                 + "\",translation=\"" + connection->escapeString(text) + "\";", connection);
}

void InsertIntoKeywordTranslations(DbConnection * const connection, const std::string &ppn,
                                   const std::string &gnd_code, const std::string &language_code,
                                   const std::string &text)
{
    ExecSqlOrDie("INSERT INTO keyword_translations SET ppn=\"" + ppn + ",gnd_code=\"" + gnd_code
                 + "\",language_code=\"" + language_code + "\", gnd_code=\"" + gnd_code + "\",translation=\""
                 + connection->escapeString(text) + "\";", connection);
}

const std::string CONF_FILE_PATH("/var/lib/tuelib/translations.conf");

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    try {
        if (argc < 2)
            Usage();

        const IniFile ini_file(CONF_FILE_PATH);
        const std::string sql_database(ini_file.getString("", "sql_database"));
        const std::string sql_username(ini_file.getString("", "sql_username"));
        const std::string sql_password(ini_file.getString("", "sql_password"));
        DbConnection db_connection(sql_database, sql_username, sql_password);

        if (std::strcmp(argv[1], "get_missing") == 0) {
            if (argc != 3)
                Error("\"get_missing\" requires exactly one argument: language_code!");
            const std::string language_code(argv[2]);
            if (not TranslationUtil::IsValidGerman3LetterCode(language_code))
                Error("\"" + language_code + "\" is not a valid 3-letter language code!");
            if (not GetMissingVuFindTranslations(&db_connection, language_code))
                GetMissingKeywordTranslations(&db_connection, language_code);
        } else if (std::strcmp(argv[1], "insert") == 0) {
            if (argc != 5 and argc != 6)
                Error("\"insert\" requires three or four arguments: token or ppn, gnd_code (if ppn), "
                      "language_code, and text!");

            const std::string language_code(argv[(argc == 6) ? 3 : 4]);
            if (not TranslationUtil::IsValidGerman3LetterCode(language_code))
                Error("\"" + language_code + "\" is not a valid German 3-letter language code!");

            if (argc == 5)
                InsertIntoVuFindTranslations(&db_connection, argv[2], language_code, argv[4]);
            else
                InsertIntoKeywordTranslations(&db_connection, argv[2], argv[3], language_code, argv[5]);
        } else
            Error("unknown command \"" + std::string(argv[1]) + "\"!");
    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Improved error reporting.<commit_after>/** \file translation_db_tool.cc
 *  \brief A tool for reading/editing of the "translations" SQL table.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2016 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <set>
#include <cstdlib>
#include <cstring>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "DbRow.h"
#include "IniFile.h"
#include "MiscUtil.h"
#include "SimpleXmlParser.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "TranslationUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " command [args]\n\n";
    std::cerr << "       Possible commands are:\n";
    std::cerr << "       get_missing language_code\n";
    std::cerr << "       insert token language_code text\n";
    std::cerr << "       insert ppn gnd_code language_code text\n";
    std::exit(EXIT_FAILURE);
}

void ExecSqlOrDie(const std::string &select_statement, DbConnection * const connection) {
    if (unlikely(not connection->query(select_statement)))
        Error("SQL Statement failed: " + select_statement + " (" + connection->getLastErrorMessage() + ")");
}

// Replaces a comma with "\," and a slash with "\\".
std::string EscapeCommasAndBackslashes(const std::string &text) {
    std::string escaped_text;
    for (const auto ch : text) {
        if (unlikely(ch == ',' or ch == '\\'))
            escaped_text += '\\';
        escaped_text += ch;
    }

    return escaped_text;
}

unsigned GetMissing(DbConnection * const connection, const std::string &table_name,
                    const std::string &table_key_name, const std::string &category,
                    const std::string &language_code)
{
     // Find a token/ppn where "language_code" is missing:
    ExecSqlOrDie("SELECT distinct " + table_key_name + " FROM " + table_name + " WHERE " + table_key_name +
                 " NOT IN (SELECT distinct " + table_key_name + " FROM " + table_name +
                 " WHERE language_code = \"" + language_code + "\") ORDER BY RAND();", connection);
    DbResultSet keys_result_set(connection->getLastResultSet());
    const size_t count(keys_result_set.size());
    if (count == 0)
        return 0;

    // Print the contents of all rows with the token from the last query on stdout:
    DbRow row(keys_result_set.getNextRow());
    const std::string matching_key(row[table_key_name]);
    ExecSqlOrDie("SELECT * FROM " + table_name + " WHERE " + table_key_name + "='" + matching_key + "';", connection);
    DbResultSet result_set(connection->getLastResultSet());
    if (result_set.empty())
        return 0;

    const std::set<std::string> column_names(SqlUtil::GetColumnNames(connection, table_name));
    const bool has_gnd_code(column_names.find("gnd_code") != column_names.cend());

    while (row = result_set.getNextRow())
        std::cout << row[table_key_name] << ',' << count << ',' << row["language_code"] << ','
                  << EscapeCommasAndBackslashes(row["translation"]) << ',' << category
                  << (has_gnd_code ? "," + row["gnd_code"] : "") << '\n';

    return result_set.size();
}

unsigned GetMissingVuFindTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "vufind_translations", "token", "vufind_translations", language_code);
}

unsigned GetMissingKeywordTranslations(DbConnection * const connection, const std::string &language_code) {
    return GetMissing(connection, "keyword_translations", "ppn", "keywords", language_code);
}

void InsertIntoVuFindTranslations(DbConnection * const connection, const std::string &token,
                                  const std::string &language_code, const std::string &text)
{
    ExecSqlOrDie("INSERT INTO vufind_translations SET token=\"" + token + "\",language_code=\"" + language_code
                 + "\",translation=\"" + connection->escapeString(text) + "\";", connection);
}

void InsertIntoKeywordTranslations(DbConnection * const connection, const std::string &ppn,
                                   const std::string &gnd_code, const std::string &language_code,
                                   const std::string &text)
{
    ExecSqlOrDie("INSERT INTO keyword_translations SET ppn=\"" + ppn + ",gnd_code=\"" + gnd_code
                 + "\",language_code=\"" + language_code + "\", gnd_code=\"" + gnd_code + "\",translation=\""
                 + connection->escapeString(text) + "\";", connection);
}

const std::string CONF_FILE_PATH("/var/lib/tuelib/translations.conf");

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    try {
        if (argc < 2)
            Usage();

        const IniFile ini_file(CONF_FILE_PATH);
        const std::string sql_database(ini_file.getString("", "sql_database"));
        const std::string sql_username(ini_file.getString("", "sql_username"));
        const std::string sql_password(ini_file.getString("", "sql_password"));
        DbConnection db_connection(sql_database, sql_username, sql_password);

        if (std::strcmp(argv[1], "get_missing") == 0) {
            if (argc != 3)
                Error("\"get_missing\" requires exactly one argument: language_code!");
            const std::string language_code(argv[2]);
            if (not TranslationUtil::IsValidGerman3LetterCode(language_code))
                Error("\"" + language_code + "\" is not a valid 3-letter language code!");
            if (not GetMissingVuFindTranslations(&db_connection, language_code))
                GetMissingKeywordTranslations(&db_connection, language_code);
        } else if (std::strcmp(argv[1], "insert") == 0) {
            if (argc != 5 and argc != 6)
                Error("\"insert\" requires three or four arguments: token or ppn, gnd_code (if ppn), "
                      "language_code, and text!");

            const std::string language_code(argv[(argc == 6) ? 3 : 4]);
            if (not TranslationUtil::IsValidGerman3LetterCode(language_code))
                Error("\"" + language_code + "\" is not a valid German 3-letter language code!");

            if (argc == 5)
                InsertIntoVuFindTranslations(&db_connection, argv[2], language_code, argv[4]);
            else
                InsertIntoKeywordTranslations(&db_connection, argv[2], argv[3], language_code, argv[5]);
        } else
            Error("unknown command \"" + std::string(argv[1]) + "\"!");
    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()) + " (login is " + MiscUtil::GetUserName() + ")");
    }
}
<|endoftext|>