refactor(PlayerDB): Improve code (#1313)
This PR includes some improvements to the player DB service: - Added `Get Gamer Online State` function pointer - Added sorting of players alphabetically and grouping of players by their online state - The player DB service will now update 32 players at a time for their online state - Player entries will automatically save when changing any data from them - Update the player online states every 5min
This commit is contained in:
@ -1,53 +1,61 @@
|
||||
#pragma once
|
||||
#include "core/data/infractions.hpp"
|
||||
#include "json_util.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class PlayerOnlineStatus
|
||||
{
|
||||
UNKNOWN,
|
||||
OFFLINE,
|
||||
ONLINE
|
||||
};
|
||||
|
||||
struct persistent_player
|
||||
{
|
||||
std::string name;
|
||||
std::uint64_t rockstar_id = 0;
|
||||
bool block_join = false;
|
||||
int block_join_reason = 1;
|
||||
bool is_modder = false;
|
||||
std::unordered_set<int> infractions;
|
||||
std::optional<CommandAccessLevel> command_access_level = std::nullopt;
|
||||
PlayerOnlineStatus online_state = PlayerOnlineStatus::UNKNOWN;
|
||||
};
|
||||
|
||||
static void to_json(nlohmann::json& j, const persistent_player& player)
|
||||
{
|
||||
j = nlohmann::json{{"name", player.name},
|
||||
{"rockstar_id", player.rockstar_id},
|
||||
{"block_join", player.block_join},
|
||||
{"block_join_reason", player.block_join_reason},
|
||||
{"is_modder", player.is_modder},
|
||||
{"infractions", player.infractions}};
|
||||
|
||||
if (player.command_access_level.has_value())
|
||||
j["command_access_level"] = player.command_access_level.value();
|
||||
};
|
||||
|
||||
static void from_json(const nlohmann::json& j, persistent_player& player)
|
||||
{
|
||||
set_from_key_or_default(j, "name", player.name);
|
||||
set_from_key_or_default(j, "rockstar_id", player.rockstar_id);
|
||||
set_from_key_or_default(j, "block_join", player.block_join);
|
||||
set_from_key_or_default(j, "block_join_reason", player.block_join_reason);
|
||||
set_from_key_or_default(j, "is_modder", player.is_modder);
|
||||
set_from_key_or_default(j, "infractions", player.infractions);
|
||||
|
||||
if (j.contains("command_access_level") && j["command_access_level"].is_string())
|
||||
player.command_access_level = j["command_access_level"].get<CommandAccessLevel>();
|
||||
}
|
||||
};
|
||||
#pragma once
|
||||
#include "core/data/infractions.hpp"
|
||||
#include "json_util.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template<typename T>
|
||||
struct adl_serializer<std::optional<T>>
|
||||
{
|
||||
static void to_json(json& j, const std::optional<big::CommandAccessLevel>& opt)
|
||||
{
|
||||
if (opt == std::nullopt)
|
||||
{
|
||||
j = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
j = *opt;
|
||||
}
|
||||
}
|
||||
|
||||
static void from_json(const json& j, std::optional<big::CommandAccessLevel>& opt)
|
||||
{
|
||||
if (j.is_null())
|
||||
{
|
||||
opt = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
opt = j.get<big::CommandAccessLevel>();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class PlayerOnlineStatus
|
||||
{
|
||||
UNKNOWN,
|
||||
OFFLINE,
|
||||
ONLINE
|
||||
};
|
||||
|
||||
struct persistent_player
|
||||
{
|
||||
std::string name;
|
||||
std::uint64_t rockstar_id = 0;
|
||||
bool block_join = false;
|
||||
int block_join_reason = 1;
|
||||
bool is_modder = false;
|
||||
std::unordered_set<int> infractions;
|
||||
std::optional<CommandAccessLevel> command_access_level = std::nullopt;
|
||||
PlayerOnlineStatus online_state = PlayerOnlineStatus::UNKNOWN;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(persistent_player, name, rockstar_id, block_join, block_join_reason, is_modder, infractions, command_access_level)
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -1,11 +1,15 @@
|
||||
#include "player_database_service.hpp"
|
||||
|
||||
#include "backend/bool_command.hpp"
|
||||
#include "file_manager.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "util/session.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
bool_command g_player_db_auto_update_online_states("player_db_auto_update_states", "Auto Update Player Online States", "Toggling this feature will automatically update the player online states every 5minutes.",
|
||||
g.player_db.update_player_online_states);
|
||||
|
||||
player_database_service::player_database_service() :
|
||||
m_file_path(g_file_manager->get_project_file("./players.json").get_path())
|
||||
{
|
||||
@ -42,35 +46,61 @@ namespace big
|
||||
file_stream >> json;
|
||||
file_stream.close();
|
||||
|
||||
for (auto& p : json.items())
|
||||
for (auto& [key, value] : json.items())
|
||||
{
|
||||
m_players[std::stoll(p.key())] = p.value().get<persistent_player>();
|
||||
auto player = value.get<std::shared_ptr<persistent_player>>();
|
||||
m_players[std::stoll(key)] = player;
|
||||
|
||||
std::string lower = player->name;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
m_sorted_players[lower] = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<std::uint64_t, persistent_player>& player_database_service::get_players()
|
||||
std::unordered_map<std::uint64_t, std::shared_ptr<persistent_player>>& player_database_service::get_players()
|
||||
{
|
||||
return m_players;
|
||||
}
|
||||
|
||||
persistent_player* player_database_service::get_player_by_rockstar_id(std::uint64_t rockstar_id)
|
||||
std::map<std::string, std::shared_ptr<persistent_player>>& player_database_service::get_sorted_players()
|
||||
{
|
||||
return m_sorted_players;
|
||||
}
|
||||
|
||||
std::shared_ptr<persistent_player> player_database_service::add_player(std::int64_t rid, const std::string_view name)
|
||||
{
|
||||
std::string lower = name.data();
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
if (m_players.contains(rid))
|
||||
{
|
||||
m_sorted_players.erase(lower);
|
||||
}
|
||||
|
||||
auto player = std::make_shared<persistent_player>(name.data(), rid);
|
||||
m_players[rid] = player;
|
||||
|
||||
m_sorted_players[lower] = player;
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
std::shared_ptr<persistent_player> player_database_service::get_player_by_rockstar_id(std::uint64_t rockstar_id)
|
||||
{
|
||||
if (m_players.contains(rockstar_id))
|
||||
return &m_players[rockstar_id];
|
||||
return m_players[rockstar_id];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
persistent_player* player_database_service::get_or_create_player(player_ptr player)
|
||||
std::shared_ptr<persistent_player> player_database_service::get_or_create_player(player_ptr player)
|
||||
{
|
||||
if (m_players.contains(player->get_net_data()->m_gamer_handle.m_rockstar_id))
|
||||
return &m_players[player->get_net_data()->m_gamer_handle.m_rockstar_id];
|
||||
return m_players[player->get_net_data()->m_gamer_handle.m_rockstar_id];
|
||||
else
|
||||
{
|
||||
m_players[player->get_net_data()->m_gamer_handle.m_rockstar_id] = {player->get_name(),
|
||||
player->get_net_data()->m_gamer_handle.m_rockstar_id};
|
||||
auto player_ptr = add_player(player->get_net_data()->m_gamer_handle.m_rockstar_id, player->get_name());
|
||||
save();
|
||||
return &m_players[player->get_net_data()->m_gamer_handle.m_rockstar_id];
|
||||
return player_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,15 +117,22 @@ namespace big
|
||||
if (m_selected && m_selected->rockstar_id == rockstar_id)
|
||||
m_selected = nullptr;
|
||||
|
||||
m_players.erase(rockstar_id);
|
||||
if (auto it = m_players.find(rockstar_id); it != m_players.end())
|
||||
{
|
||||
std::string lower = it->second->name;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
m_sorted_players.erase(lower);
|
||||
m_players.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void player_database_service::set_selected(persistent_player* selected)
|
||||
void player_database_service::set_selected(std::shared_ptr<persistent_player> selected)
|
||||
{
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
persistent_player* player_database_service::get_selected()
|
||||
std::shared_ptr<persistent_player> player_database_service::get_selected()
|
||||
{
|
||||
return m_selected;
|
||||
}
|
||||
@ -103,35 +140,70 @@ namespace big
|
||||
void player_database_service::invalidate_player_states()
|
||||
{
|
||||
for (auto& item : m_players)
|
||||
item.second.online_state = PlayerOnlineStatus::UNKNOWN;
|
||||
item.second->online_state = PlayerOnlineStatus::UNKNOWN;
|
||||
}
|
||||
|
||||
void player_database_service::start_update_loop()
|
||||
{
|
||||
if (!g.player_db.update_player_online_states)
|
||||
return;
|
||||
|
||||
g_thread_pool->push([this] {
|
||||
static auto last_update = std::chrono::high_resolution_clock::now() - 5min;
|
||||
while (g_running && g.player_db.update_player_online_states)
|
||||
{
|
||||
const auto cur = std::chrono::high_resolution_clock::now();
|
||||
if (cur - last_update > 5min)
|
||||
{
|
||||
g_fiber_pool->queue_job([this] {
|
||||
update_player_states();
|
||||
});
|
||||
last_update = cur;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void player_database_service::update_player_states()
|
||||
{
|
||||
invalidate_player_states();
|
||||
const auto player_count = m_players.size();
|
||||
|
||||
//fetch current stat for each player.. this will need some time.
|
||||
for (auto& item : m_players)
|
||||
std::vector<std::vector<rage::rlGamerHandle>> gamer_handle_buckets;
|
||||
gamer_handle_buckets.resize(std::ceil(player_count / 32.f));
|
||||
|
||||
auto it = m_players.begin();
|
||||
for (size_t i = 0; i < player_count; ++i)
|
||||
{
|
||||
auto& player = item.second;
|
||||
rage::rlGamerHandle player_handle(player.rockstar_id);
|
||||
rage::rlSessionByGamerTaskResult result;
|
||||
bool success = false;
|
||||
rage::rlTaskStatus state{};
|
||||
gamer_handle_buckets[i / 32].push_back(it->second->rockstar_id);
|
||||
|
||||
if (g_pointers->m_gta.m_start_get_session_by_gamer_handle(0, &player_handle, 1, &result, 1, &success, &state))
|
||||
it++;
|
||||
}
|
||||
|
||||
for (auto& bucket : gamer_handle_buckets)
|
||||
{
|
||||
rage::rlTaskStatus status;
|
||||
std::array<int, 32> online;
|
||||
|
||||
if (g_pointers->m_gta.m_get_gamer_online_state(0, bucket.data(), bucket.size(), online.data(), &status))
|
||||
{
|
||||
while (state.status == 1)
|
||||
script::get_current()->yield();
|
||||
|
||||
if (state.status == 3 && success)
|
||||
while (status.status == 1)
|
||||
{
|
||||
player.online_state = PlayerOnlineStatus::ONLINE;
|
||||
continue;
|
||||
script::get_current()->yield();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bucket.size(); ++i)
|
||||
{
|
||||
if (const auto& it = m_players.find(bucket[i].m_rockstar_id); it != m_players.end())
|
||||
{
|
||||
it->second->online_state = PlayerOnlineStatus::OFFLINE;
|
||||
if (online[i] == 1)
|
||||
it->second->online_state = PlayerOnlineStatus::ONLINE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player.online_state = PlayerOnlineStatus::OFFLINE;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +1,56 @@
|
||||
#pragma once
|
||||
#include "persistent_player.hpp"
|
||||
#include "services/players/player.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class player_database_service
|
||||
{
|
||||
std::unordered_map<std::uint64_t, persistent_player> m_players;
|
||||
persistent_player* m_selected = nullptr;
|
||||
|
||||
public:
|
||||
std::filesystem::path m_file_path;
|
||||
player_database_service();
|
||||
~player_database_service();
|
||||
|
||||
void save();
|
||||
void load();
|
||||
|
||||
std::unordered_map<std::uint64_t, persistent_player>& get_players();
|
||||
persistent_player* get_player_by_rockstar_id(std::uint64_t rockstar_id);
|
||||
persistent_player* get_or_create_player(player_ptr player);
|
||||
void update_rockstar_id(std::uint64_t old, std::uint64_t _new);
|
||||
void remove_rockstar_id(std::uint64_t rockstar_id);
|
||||
|
||||
void set_selected(persistent_player* selected);
|
||||
persistent_player* get_selected();
|
||||
|
||||
void update_player_states();
|
||||
void invalidate_player_states();
|
||||
};
|
||||
|
||||
inline player_database_service* g_player_database_service;
|
||||
#pragma once
|
||||
#include "persistent_player.hpp"
|
||||
#include "services/players/player.hpp"
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template<typename T>
|
||||
struct adl_serializer<std::shared_ptr<T>>
|
||||
{
|
||||
static void to_json(json& j, const std::shared_ptr<T>& value)
|
||||
{
|
||||
j = *value;
|
||||
}
|
||||
|
||||
static void from_json(const json& j, std::shared_ptr<T>& value)
|
||||
{
|
||||
value = std::make_shared<T>();
|
||||
*value = j.get<T>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace big
|
||||
{
|
||||
class player_database_service
|
||||
{
|
||||
std::unordered_map<std::uint64_t, std::shared_ptr<persistent_player>> m_players;
|
||||
std::map<std::string, std::shared_ptr<persistent_player>> m_sorted_players;
|
||||
std::shared_ptr<persistent_player> m_selected = nullptr;
|
||||
|
||||
public:
|
||||
std::filesystem::path m_file_path;
|
||||
player_database_service();
|
||||
~player_database_service();
|
||||
|
||||
void save();
|
||||
void load();
|
||||
|
||||
std::shared_ptr<persistent_player> add_player(std::int64_t rid, const std::string_view name);
|
||||
std::unordered_map<std::uint64_t, std::shared_ptr<persistent_player>>& get_players();
|
||||
std::map<std::string, std::shared_ptr<persistent_player>>& get_sorted_players();
|
||||
std::shared_ptr<persistent_player> get_player_by_rockstar_id(std::uint64_t rockstar_id);
|
||||
std::shared_ptr<persistent_player> get_or_create_player(player_ptr player);
|
||||
void update_rockstar_id(std::uint64_t old, std::uint64_t _new);
|
||||
void remove_rockstar_id(std::uint64_t rockstar_id);
|
||||
|
||||
void set_selected(std::shared_ptr<persistent_player> selected);
|
||||
std::shared_ptr<persistent_player> get_selected();
|
||||
|
||||
void start_update_loop();
|
||||
void update_player_states();
|
||||
void invalidate_player_states();
|
||||
};
|
||||
|
||||
inline player_database_service* g_player_database_service;
|
||||
}
|
Reference in New Issue
Block a user