Profileinfo v2 (#114)

* profile info (todo)

* fix path & server running check

* temp progress

* more progress idek i need xuids

* profile info v2

* fix bdProfiles reading bad data

* custom emblem storage + cleanup

* tiny bit of progress but stalling in buffer read

* stuff

* small stuff

* Update profile_infos.cpp

* update

* player xuid packet, stuff... kinda works?

* works

* cleanup

* fix

* cleanup

* cleanup on aisle 2

---------

Co-authored-by: m <mjkzyalt@gmail.com>
This commit is contained in:
quaK 2024-01-31 23:48:03 +02:00 committed by GitHub
parent c8a99d23c1
commit 08b4eec837
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1424 additions and 254 deletions

View File

@ -4,9 +4,11 @@
#include "network.hpp"
#include "game/game.hpp"
#include "game/utils/fragment_handler.hpp"
#include "console/console.hpp"
#include "dvars.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -30,16 +32,27 @@ namespace network
auto& callbacks = get_callbacks();
const auto handler = callbacks.find(cmd_string);
const auto offset = cmd_string.size() + 5;
if (message->cursize < offset || handler == callbacks.end())
if (message->cursize < 0 || static_cast<size_t>(message->cursize) < offset || handler == callbacks.end())
{
return false;
}
const std::string_view data(message->data + offset, message->cursize - offset);
const std::basic_string_view data(message->data + offset, message->cursize - offset);
//console::debug("[Network] Handling command %s\n", cmd_string.data());
console::debug("[network] handling command \"%s\"\n", cmd_string.data());
try
{
handler->second(*address, data);
}
catch (const std::exception& e)
{
printf("Error: %s\n", e.what());
}
//catch (...)
//{
//}
return true;
}
@ -232,15 +245,14 @@ namespace network
void send_data(const game::netadr_s& address, const std::string& data)
{
auto size = static_cast<int>(data.size());
if (address.type == game::NA_LOOPBACK)
{
// TODO: Fix this for loopback
if (size > 1280)
{
console::error("Packet was too long. Truncated!\n");
size = 1280;
}
if (address.type == game::NA_LOOPBACK)
{
game::NET_SendLoopPacket(game::NS_CLIENT1, size, data.data(), &address);
}
else
@ -284,6 +296,8 @@ namespace network
public:
void post_unpack() override
{
scheduler::loop(game::fragment_handler::clean, scheduler::async, 5s);
// redirect dw packet sends to our stub
utils::hook::jump(0xD942C0_b, dw_send_to_stub);

View File

@ -10,6 +10,7 @@
#include "command.hpp"
#include "console/console.hpp"
#include "network.hpp"
#include "profile_infos.hpp"
#include "scheduler.hpp"
#include <utils/string.hpp>
@ -21,12 +22,7 @@ namespace party
{
namespace
{
struct
{
game::netadr_s host{};
std::string challenge{};
bool hostDefined{ false };
} connect_state;
connection_state server_connection_state{};
bool preloaded_map = false;
@ -165,6 +161,8 @@ namespace party
void sv_start_map_for_party_stub(const char* map, const char* game_type, int client_count, int agent_count, bool hardcore,
bool map_is_preloaded, bool migrate)
{
profile_infos::xuid::clear_xuids();
preloaded_map = map_is_preloaded;
sv_start_map_for_party_hook.invoke<void>(map, game_type, client_count, agent_count, hardcore, map_is_preloaded, migrate);
}
@ -196,7 +194,7 @@ namespace party
a.popad64();
a.jmp(0xC563E2_b);
};
}
}
void start_map(const std::string& mapname, bool dev)
@ -313,26 +311,30 @@ namespace party
void connect(const game::netadr_s& target)
{
//command::execute("lui_open_popup popup_acceptinginvite", false);
command::execute("luiOpenPopup AcceptingInvite", false);
connect_state.host = target;
connect_state.challenge = utils::cryptography::random::get_challenge();
connect_state.hostDefined = true;
profile_infos::xuid::clear_xuids();
profile_infos::clear_profile_infos();
network::send(target, "getInfo", connect_state.challenge);
server_connection_state.host = target;
server_connection_state.challenge = utils::cryptography::random::get_challenge();
server_connection_state.hostDefined = true;
network::send(target, "getInfo", server_connection_state.challenge);
}
void info_response_error(const std::string& error)
{
console::error("%s\n", error.data());
//if (game::Menu_IsMenuOpenAndVisible(0, "popup_acceptinginvite"))
//{
// command::execute("lui_close popup_acceptinginvite", false);
//}
command::execute("luiLeaveMenu AcceptingInvite", false);
game::Com_SetLocalizedErrorMessage(error.data(), "MENU_NOTICE");
}
connection_state get_server_connection_state()
{
return server_connection_state;
}
class component final : public component_interface
{
public:
@ -483,45 +485,45 @@ namespace party
network::on("infoResponse", [](const game::netadr_s& target, const std::string_view& data)
{
const utils::info_string info{ data };
const utils::info_string info = data;
//server_list::handle_info_response(target, info);
if (connect_state.host != target)
if (server_connection_state.host != target)
{
return;
}
if (info.get("challenge") != connect_state.challenge)
if (info.get("challenge") != server_connection_state.challenge)
{
info_response_error("Invalid challenge.");
info_response_error("Connection failed: Invalid challenge.");
return;
}
const auto gamename = info.get("gamename");
if (gamename != "IW7"s)
{
info_response_error("Invalid gamename.");
info_response_error("Connection failed: Invalid gamename.");
return;
}
const auto playmode = info.get("playmode");
if (game::GameModeType(std::atoi(playmode.data())) != game::Com_GameMode_GetActiveGameMode())
{
info_response_error("Invalid playmode.");
info_response_error("Connection failed: Invalid playmode.");
return;
}
const auto sv_running = info.get("sv_running");
if (!std::atoi(sv_running.data()))
{
info_response_error("Server not running.");
info_response_error("Connection failed: Server not running.");
return;
}
const auto mapname = info.get("mapname");
if (mapname.empty())
{
info_response_error("Invalid map.");
info_response_error("Connection failed: Invalid map.");
return;
}
@ -536,12 +538,21 @@ namespace party
const auto sv_maxclients = std::atoi(sv_maxclients_str.data());
if (!sv_maxclients)
{
info_response_error("Invalid sv_maxclients.");
info_response_error("Connection failed: Invalid sv_maxclients.");
return;
}
//party::sv_motd = info.get("sv_motd");
//party::sv_maxclients = std::stoi(info.get("sv_maxclients"));
server_connection_state.motd = info.get("sv_motd");
server_connection_state.max_clients = std::stoi(sv_maxclients_str);
const auto profile_info = profile_infos::get_profile_info();
if (!profile_info.has_value())
{
console::error("profile_info has no value to send, possible undefined behavior ahead\n");
}
const auto profile_info_value = profile_info.value_or(profile_infos::profile_info{});
profile_infos::send_profile_info(target, steam::SteamUser()->GetSteamID().bits, profile_info_value);
connect_to_party(target, mapname, gametype, sv_maxclients);
});

View File

@ -3,17 +3,21 @@
namespace party
{
void info_response_error(const std::string& error);
struct connection_state
{
game::netadr_s host;
std::string challenge;
bool hostDefined;
std::string motd;
int max_clients;
};
void reset_connect_state();
void info_response_error(const std::string& error);
void connect(const game::netadr_s& target);
void start_map(const std::string& mapname, bool dev = false);
void clear_sv_motd();
game::netadr_s get_state_host();
std::string get_state_challenge();
int server_client_count();
connection_state get_server_connection_state();
int get_client_num_by_name(const std::string& name);

View File

@ -0,0 +1,426 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "../steam/steam.hpp"
#include "dvars.hpp"
#include "console/console.hpp"
#include "network.hpp"
#include "party.hpp"
#include "profile_infos.hpp"
#include <utils/concurrency.hpp>
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include "game/utils/fragment_handler.hpp"
namespace profile_infos
{
namespace
{
using profile_map = std::unordered_map<std::uint64_t, profile_info>;
utils::concurrency::container<profile_map, std::recursive_mutex> profile_mapping{};
std::optional<profile_info> load_profile_info()
{
std::string data{};
if (!utils::io::read_file("players2/user/profile_info", &data))
{
console::error("[load_profile_info] failed to load profile_info for self!\n");
return {};
}
profile_info info{};
info.m_memberplayer_card.assign(data);
return {std::move(info)};
}
std::unordered_set<std::uint64_t> get_connected_client_xuids()
{
if (!game::Com_IsAnyLocalServerRunning()) // is_host()
{
return {};
}
std::unordered_set<std::uint64_t> xuids{};
const auto* svs_clients = *game::svs_clients;
for (unsigned int i = 0; i < *game::svs_numclients; ++i)
{
if (svs_clients[i].header.state >= 1)
{
xuids.emplace(xuid::get_client_xuid(i));
}
}
return xuids;
}
void set_playercardcache_to_download(const std::uint64_t user_id)
{
game::XUID xuid{ user_id };
game::PlayercardCache_AddToDownload(0, xuid);
*game::g_DWPlayercardCacheDownloadTaskStage = game::PLAYERCARD_CACHE_TASK_STAGE_WAITING;
}
void set_client_xuid_to_session(game::SessionData* session, const std::uint32_t client_index)
{
session->dyn.users[client_index].xuid = xuid::get_client_xuid(client_index);
}
}
profile_info::profile_info(utils::byte_buffer& buffer)
{
this->m_memberplayer_card = buffer.read_string();
}
void profile_info::serialize(utils::byte_buffer& buffer) const
{
buffer.write_string(this->m_memberplayer_card);
}
void clear_profile_infos()
{
profile_mapping.access([](profile_map& profiles)
{
profiles.clear();
});
}
void remove_profile_info(const std::uint64_t user_id)
{
profile_mapping.access([user_id](profile_map& profiles)
{
for (auto i = profiles.begin(); i != profiles.end();)
{
if (i->first == user_id)
{
i = profiles.erase(i);
}
else
{
i++;
}
}
});
}
void remove_profile_info_by_client_index(const std::uint32_t client_index)
{
const auto user_id = xuid::get_client_xuid(client_index);
if (!user_id)
{
return;
}
remove_profile_info(user_id);
}
void send_profile_info(const game::netadr_s& address, const std::string& data)
{
game::fragment_handler::fragment_data(data.data(), data.size(), [&address](const utils::byte_buffer& buffer)
{
network::send(address, "profileInfo", buffer.get_buffer());
});
}
void send_profile_info(const game::netadr_s& address, const std::uint64_t user_id, const profile_info& info)
{
utils::byte_buffer buffer{};
buffer.write(user_id);
info.serialize(buffer);
const std::string data = buffer.move_buffer();
send_profile_info(address, data);
}
std::optional<profile_info> get_profile_info()
{
return load_profile_info();
}
std::optional<profile_info> get_profile_info(const uint64_t user_id)
{
const auto my_xuid = steam::SteamUser()->GetSteamID().bits;
if (user_id == my_xuid)
{
return get_profile_info();
}
return profile_mapping.access<std::optional<profile_info>>([user_id](profile_map& profiles)
{
std::optional<profile_info> result{};
const auto profile_entry = profiles.find(user_id);
if (profile_entry != profiles.end())
{
result = profile_entry->second;
}
#ifdef DEBUG
else
{
console::error("[get_profile_info] requesting profile info for %llX (bad)\n", user_id);
}
#endif
return result;
});
}
void update_profile_info(const profile_info& info)
{
std::string data{};
data.reserve(info.m_memberplayer_card.size());
data.append(info.m_memberplayer_card);
utils::io::write_file("players2/user/profile_info", data);
}
void send_all_profile_infos(const game::netadr_s& sender_addr)
{
profile_mapping.access([&](const profile_map& profiles)
{
for (const auto& entry : profiles)
{
send_profile_info(sender_addr, entry.first, entry.second);
}
});
}
void send_profile_info_to_all_clients(const std::uint64_t user_id, const profile_info& info)
{
const auto* svs_clients = *game::svs_clients;
for (unsigned int i = 0; i < *game::svs_numclients; ++i)
{
if (svs_clients[i].header.state >= 1 && !game::SV_BotIsBot(i) && !game::Session_IsHost(game::SV_MainMP_GetServerLobby(), i))
{
send_profile_info(svs_clients[i].remoteAddress, user_id, info);
}
}
}
void send_self_profile(const game::netadr_s& addr)
{
const auto* svs_clients = *game::svs_clients;
for (unsigned int i = 0; i < *game::svs_numclients; ++i)
{
if (svs_clients[i].header.state >= 1 && !game::SV_BotIsBot(i) && game::Session_IsHost(game::SV_MainMP_GetServerLobby(), i))
{
assert(i == 0);
auto self = load_profile_info();
if (self.has_value())
{
send_profile_info(addr, xuid::get_client_xuid(i), self.value());
break;
}
}
}
}
void add_profile_info(const game::netadr_s& sender_addr, const std::uint64_t user_id, const profile_info& info)
{
if (user_id == steam::SteamUser()->GetSteamID().bits)
{
return;
}
if (game::Com_IsAnyLocalServerRunning()) // is_host()
{
send_all_profile_infos(sender_addr); // send all stored profile infos to the new player
if (!game::environment::is_dedi())
{
send_self_profile(sender_addr); // send self profile info to the new player too
}
// send new player info to all clients
send_profile_info_to_all_clients(user_id, info);
}
profile_mapping.access([&](profile_map& profiles)
{
profiles[user_id] = info;
});
}
namespace xuid
{
client_xuid_array client_xuids{};
void add_client_xuid(const std::uint32_t& client_index, const std::uint64_t& xuid)
{
if (client_xuids[client_index] && client_xuids[client_index] != xuid)
{
remove_profile_info(client_xuids[client_index]); // remove profile if it exists
}
client_xuids[client_index] = xuid;
set_client_xuid_to_session(game::SV_MainMP_GetServerLobby(), client_index);
}
std::uint64_t get_client_xuid(const std::uint32_t& client_index)
{
if (client_xuids[client_index])
{
// returns xuid for player. this must be on both the client & server
// client recieves data for this via playerXuid packet
return client_xuids[client_index];
}
return static_cast<std::uint64_t>(0);
}
void remove_client_xuid(const std::uint32_t& client_index)
{
client_xuids[client_index] = 0;
}
void clear_xuids()
{
for (auto& xuid : client_xuids)
{
xuid = 0;
}
}
client_xuid_array get_xuids()
{
return client_xuids;
}
void send_xuid(const game::netadr_s& addr, const std::uint64_t xuid, const std::uint32_t client_index)
{
utils::byte_buffer buffer{};
buffer.write(client_index);
buffer.write(xuid);
const std::string data = buffer.move_buffer();
game::fragment_handler::fragment_data(data.data(), data.size(), [&](const utils::byte_buffer& buffer)
{
network::send(addr, "playerXuid", buffer.get_buffer());
});
}
void send_xuid_to_all_clients(const std::uint64_t xuid, const std::uint32_t& client_index)
{
const auto* svs_clients = *game::svs_clients;
for (unsigned int i = 0; i < *game::svs_numclients; ++i)
{
if (svs_clients[i].header.state >= 1 && !game::SV_BotIsBot(i) && !game::Session_IsHost(game::SV_MainMP_GetServerLobby(), i))
{
send_xuid(svs_clients[i].remoteAddress, xuid, client_index);
}
}
}
void send_all_xuids(const game::netadr_s& addr)
{
int i = 0;
for (const auto xuid : xuid::get_xuids())
{
if (xuid == 0)
{
continue;
}
send_xuid(addr, xuid, i++);
}
if (!game::environment::is_dedi())
{
// send self xuid here too
send_xuid(addr, steam::SteamUser()->GetSteamID().bits, 0);
}
}
}
namespace
{
utils::hook::detour client_connect_hook;
const char* client_connect_stub(int client_num, unsigned __int16 script_pers_id)
{
auto result = client_connect_hook.invoke<const char*>(client_num, script_pers_id);
const auto client = game::svs_clients[client_num];
std::uint64_t xuid{};
game::StringToXUID(client->playerGuid, &xuid);
xuid::add_client_xuid(client_num, xuid); // add to self
// don't send if client is self
if (client_num == 0 && !game::environment::is_dedi() && game::Com_IsAnyLocalServerRunning())
{
return result;
}
xuid::send_xuid_to_all_clients(xuid, client_num); // add to all connected
xuid::send_all_xuids(client->remoteAddress);
return result;
}
utils::hook::detour session_unregister_remote_player_hook;
void session_unregister_remote_player_stub(game::SessionData* session, const int slot)
{
session_unregister_remote_player_hook.invoke<void>(session, slot);
set_client_xuid_to_session(game::SV_MainMP_GetServerLobby(), slot);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
client_connect_hook.create(0xAFFF10_b, client_connect_stub);
session_unregister_remote_player_hook.create(0xC73970_b, session_unregister_remote_player_stub);
dvars::override::register_int("playercard_cache_validity_life", 5000, 0, 3600000, 0x0); // 5sec
network::on("profileInfo", [](const game::netadr_s& client_addr, const std::string_view& data)
{
utils::byte_buffer buffer(data);
std::string final_packet{};
if (game::fragment_handler::handle(client_addr, buffer, final_packet))
{
buffer = utils::byte_buffer(final_packet);
const auto user_id = buffer.read<uint64_t>();
const profile_info info(buffer);
add_profile_info(client_addr, user_id, info);
set_playercardcache_to_download(user_id);
}
});
network::on("playerXuid", [](const game::netadr_s& server_addr, const std::string_view& data)
{
utils::byte_buffer buffer(data);
std::string final_packet{};
if (!game::fragment_handler::handle(server_addr, buffer, final_packet))
{
return;
}
buffer = utils::byte_buffer(final_packet);
const auto client_index = buffer.read<std::uint32_t>();
const auto xuid = buffer.read<std::uint64_t>();
if (!game::Com_IsAnyLocalServerRunning() && server_addr.addr != party::get_server_connection_state().host.addr)
{
console::debug("playerXuid call from an unknown address\n");
return;
}
xuid::add_client_xuid(client_index, xuid);
});
}
};
}
REGISTER_COMPONENT(profile_infos::component)

View File

@ -0,0 +1,33 @@
#pragma once
#include <game/game.hpp>
#include <utils/byte_buffer.hpp>
namespace profile_infos
{
namespace xuid
{
using client_xuid_array = std::array<std::uint64_t, 18>;
std::uint64_t get_client_xuid(const std::uint32_t& client_index);
void clear_xuids();
client_xuid_array get_xuids();
}
struct profile_info
{
std::string m_memberplayer_card{};
profile_info() = default;
profile_info(utils::byte_buffer& buffer);
void serialize(utils::byte_buffer& buffer) const;
};
void clear_profile_infos();
void send_profile_info(const game::netadr_s& address, const std::uint64_t user_id, const profile_info& info);
std::optional<profile_info> get_profile_info();
std::optional<profile_info> get_profile_info(const uint64_t user_id);
void update_profile_info(const profile_info& info);
}

View File

@ -1,5 +1,4 @@
#pragma once
#include "dw_include.hpp"
namespace demonware
{
@ -243,4 +242,122 @@ namespace demonware
buffer->read_blob(&this->data);
}
};
class bdContextUserStorageFileInfo final : public bdTaskResult
{
public:
std::uint32_t create_time;
std::uint32_t modifed_time;
bool priv;
std::uint64_t owner_id;
std::string account_type;
std::string filename;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint32(this->create_time);
buffer->write_uint32(this->modifed_time);
buffer->write_bool(this->priv);
buffer->write_uint64(this->owner_id);
buffer->write_string(this->account_type);
buffer->write_string(this->filename);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint32(&this->create_time);
buffer->read_uint32(&this->modifed_time);
buffer->read_bool(&this->priv);
buffer->read_uint64(&this->owner_id);
buffer->read_string(&this->account_type);
buffer->read_string(&this->filename);
}
};
class bdPublicProfileInfo final : public bdTaskResult
{
public:
std::uint64_t m_entityID;
std::string m_memberplayer_card;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint64(this->m_entityID);
buffer->write_blob(this->m_memberplayer_card);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint64(&this->m_entityID);
buffer->read_blob(&this->m_memberplayer_card);
}
};
class bdSessionID final : public bdTaskResult
{
public:
uint64_t session_id;
void serialize(byte_buffer* buffer) override
{
buffer->write_blob(LPSTR(&this->session_id), sizeof this->session_id);
}
void deserialize(byte_buffer* buffer) override
{
int size{};
char* data{};
buffer->read_blob(&data, &size);
if (data && uint32_t(size) >= sizeof(this->session_id))
{
this->session_id = *reinterpret_cast<uint64_t*>(data);
}
}
};
class bdMatchMakingInfo final : bdTaskResult
{
bdSessionID m_sessionID;
std::string m_hostAddr;
uint32_t m_hostAddrSize;
uint32_t m_gameType;
uint32_t m_maxPlayers;
uint32_t m_numPlayers;
void serialize(byte_buffer* buffer) override
{
buffer->write_blob(this->m_hostAddr);
this->m_sessionID.serialize(buffer);
buffer->write_uint32(this->m_gameType);
buffer->write_uint32(this->m_maxPlayers);
buffer->write_uint32(this->m_numPlayers);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_blob(&this->m_hostAddr);
buffer->read_uint32(&this->m_gameType);
buffer->read_uint32(&this->m_maxPlayers);
}
};
class bdPerformanceValue final : public bdTaskResult
{
public:
uint64_t user_id;
int64_t performance;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint64(this->user_id);
buffer->write_int64(this->performance);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint64(&this->user_id);
buffer->read_int64(&this->performance);
}
};
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <utils/string.hpp>
#include "servers/service_server.hpp"
namespace demonware

View File

@ -1,6 +1,8 @@
#include <std_include.hpp>
#include "../dw_include.hpp"
#include "steam/steam.hpp"
namespace demonware
{
bdMatchMaking::bdMatchMaking() : service(138, "bdMatchMaking")
@ -24,8 +26,11 @@ namespace demonware
void bdMatchMaking::createSession(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto id = std::make_unique<bdSessionID>();
id->session_id = steam::SteamUser()->GetSteamID().bits;
auto reply = server->create_reply(this->task_id());
reply.add(id);
reply.send();
}
@ -36,9 +41,14 @@ namespace demonware
reply.send();
}
void bdMatchMaking::deleteSession(service_server* server, byte_buffer* /*buffer*/) const
void bdMatchMaking::deleteSession(service_server* server, byte_buffer* buffer) const
{
// TODO:
bdSessionID id;
id.deserialize(buffer);
byte_buffer out_data;
id.serialize(&out_data);
auto reply = server->create_reply(this->task_id());
reply.send();
}
@ -59,8 +69,12 @@ namespace demonware
void bdMatchMaking::getPerformanceValues(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto result = std::make_unique<bdPerformanceValue>();
result->user_id = steam::SteamUser()->GetSteamID().bits;
result->performance = 10;
auto reply = server->create_reply(this->task_id());
reply.add(result);
reply.send();
}

View File

@ -1,6 +1,8 @@
#include <std_include.hpp>
#include "../dw_include.hpp"
#include "../../../component/profile_infos.hpp"
namespace demonware
{
bdProfiles::bdProfiles() : service(8, "bdProfiles")
@ -15,16 +17,40 @@ namespace demonware
this->register_task(8, &bdProfiles::setPublicInfoByUserID);
}
void bdProfiles::getPublicInfos(service_server* server, byte_buffer* /*buffer*/) const
void bdProfiles::getPublicInfos(service_server* server, byte_buffer* buffer) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
std::vector<std::pair<std::uint64_t, profile_infos::profile_info>> profile_infos{};
std::uint64_t entity_id;
while (buffer->read_uint64(&entity_id))
{
auto profile = profile_infos::get_profile_info(entity_id);
if (profile)
{
profile_infos.emplace_back(entity_id, std::move(*profile));
}
}
auto reply = server->create_reply(this->task_id(), profile_infos.empty() ? game::BD_NO_PROFILE_INFO_EXISTS : game::BD_NO_ERROR);
for (auto& info : profile_infos)
{
auto result = std::make_unique<bdPublicProfileInfo>();
result->m_entityID = info.first;
result->m_memberplayer_card = std::move(info.second.m_memberplayer_card);
reply.add(result);
}
reply.send();
}
void bdProfiles::setPublicInfo(service_server* server, byte_buffer* /*buffer*/) const
void bdProfiles::setPublicInfo(service_server* server, byte_buffer* buffer) const
{
// TODO:
profile_infos::profile_info info{};
buffer->read_blob(&info.m_memberplayer_card);
profile_infos::update_profile_info(info);
auto reply = server->create_reply(this->task_id());
reply.send();
}

View File

@ -14,6 +14,7 @@ namespace demonware
this->register_task(20, &bdStorage::listAllPublisherFiles);
this->register_task(21, &bdStorage::getPublisherFile);
this->register_task(24, &bdStorage::uploadAndValidateFiles);
this->register_task(18, &bdStorage::uploadFiles);
this->register_task(16, &bdStorage::getFiles);
this->register_task(12, &bdStorage::getFile);
@ -173,6 +174,51 @@ namespace demonware
info->filename = filename;
info->data = data;
#ifdef DW_DEBUG
printf("[DW]: [bdStorage]: set user file: %s\n", filename.data());
#endif
reply.add(info);
}
reply.send();
}
void bdStorage::uploadFiles(service_server* server, byte_buffer* buffer) const
{
std::string game, platform;
std::uint64_t owner;
std::uint32_t numfiles;
buffer->read_string(&game);
buffer->read_uint64(&owner);
buffer->read_string(&platform);
buffer->read_uint32(&numfiles);
auto reply = server->create_reply(this->task_id());
for (uint32_t i = 0; i < numfiles; i++)
{
std::string filename, data;
std::uint32_t version;
bool priv;
buffer->read_string(&filename);
buffer->read_blob(&data);
buffer->read_uint32(&version);
buffer->read_bool(&priv);
const auto path = get_user_file_path(filename);
utils::io::write_file(path, data);
auto info = std::make_unique<bdContextUserStorageFileInfo>();
info->modifed_time = static_cast<uint32_t>(time(nullptr));
info->create_time = info->modifed_time;
info->priv = priv;
info->owner_id = owner;
info->account_type = platform;
info->filename = filename;
#ifdef DW_DEBUG
printf("[DW]: [bdStorage]: set user file: %s\n", filename.data());
#endif
@ -186,26 +232,26 @@ namespace demonware
void bdStorage::getFiles(service_server* server, byte_buffer* buffer) const
{
std::string platform;
uint32_t numunk;
uint32_t numfiles;
uint32_t num_users;
uint32_t num_files;
uint64_t user_id = 0;
std::string game;
buffer->read_string(&platform);
buffer->read_uint32(&numunk);
buffer->read_uint32(&num_users);
for (uint32_t i = 0; i < numunk; i++)
for (uint32_t i = 0; i < num_users; i++)
{
buffer->read_uint64(&user_id);
buffer->read_string(&game);
}
buffer->read_uint32(&numfiles);
buffer->read_uint32(&num_files);
auto reply = server->create_reply(this->task_id());
uint32_t count = 0;
for (uint32_t i = 0; i < numfiles; i++)
for (uint32_t i = 0; i < num_files; i++)
{
std::string filename, data;
buffer->read_string(&filename);

View File

@ -7,6 +7,8 @@ namespace demonware
public:
bdStorage();
static std::string get_user_file_path(const std::string& name);
private:
using callback = std::function<std::string()>;
using resource_variant = std::variant<std::string, callback>;
@ -19,9 +21,8 @@ namespace demonware
void listAllPublisherFiles(service_server* server, byte_buffer* buffer);
void getPublisherFile(service_server* server, byte_buffer* buffer);
void uploadAndValidateFiles(service_server* server, byte_buffer* buffer) const;
void uploadFiles(service_server* server, byte_buffer* buffer) const;
void getFiles(service_server* server, byte_buffer* buffer) const;
void getFile(service_server* server, byte_buffer* buffer) const;
static std::string get_user_file_path(const std::string& name);
};
}

View File

@ -1,19 +1,16 @@
#pragma once
#include "types/database.hpp"
#include "types/ddl.hpp"
#include "types/demonware.hpp"
#include "types/party.hpp"
#include "types/pmem.hpp"
#include "types/sv.hpp"
namespace game
{
using namespace database;
using namespace ddl;
using namespace demonware;
using namespace party;
using namespace pmem;
using namespace sv;
struct XUID
{
std::uint64_t m_id;
};
enum GameModeType : std::uint32_t
{
@ -27,6 +24,7 @@ namespace game
{
FEATURE_GRAVITY = 33,
FEATURE_TIMESCALE = 69,
FEATURE_RANDOM_PLAYERCARD_WHEN_MISSING = 163,
};
enum Sys_Folder
@ -392,7 +390,11 @@ namespace game
struct netadr_s
{
netadrtype_t type;
union
{
unsigned char ip[4];
uint32_t addr;
};
unsigned __int16 port;
netsrc_t localNetID;
unsigned int addrHandleIndex;
@ -500,8 +502,13 @@ namespace game
struct entityState_t
{
__int16 number; // 0
char __pad0[150];
int surfType;
int clientNum;
}; // sizeof = ?
assert_offsetof(entityState_t, clientNum, 156);
struct gclient_s
{
char __pad0[19376];
@ -547,14 +554,16 @@ namespace game
struct client_t
{
clientHeader_t header; // 0
char __pad0[120];
char __pad0[120]; // 16
gentity_s* gentity; // 136
char __pad1[20];
char userinfo[1024];
char name[32]; // 1188
char __pad2[648396];
char __pad2[648396]; // 1220
netadr_s remoteAddress; // 649616
char __pad3[65780];
char __pad5[2460]; // 649636
char playerGuid[21]; // 652096
char __pad6[63299]; // 652117
}; static_assert(sizeof(client_t) == 715416);
static_assert(offsetof(client_t, header.state) == 8);
@ -562,6 +571,7 @@ namespace game
static_assert(offsetof(client_t, userinfo) == 164);
static_assert(offsetof(client_t, name) == 1188);
static_assert(offsetof(client_t, remoteAddress) == 649616);
static_assert(offsetof(client_t, playerGuid) == 652096);
}
using namespace entity;
@ -788,4 +798,254 @@ namespace game
unsigned __int16 childVariableBucket[65536];
ChildVariableValue childVariableValue[384000];
};
enum PLAYERCARD_CACHE_TASK_STAGE
{
PLAYERCARD_CACHE_TASK_STAGE_WAITING = 0x0,
PLAYERCARD_CACHE_TASK_STAGE_WORKING = 0x1,
PLAYERCARD_CACHE_TASK_STAGE_ALL_DONE = 0x2,
};
struct CachedPlayerProfile
{
bool has_data;
XUID userID;
char profile[2201];
int time;
};
namespace ddl
{
enum DDLType
{
DDL_INVALID_TYPE = 0xFFFFFFFF,
DDL_BYTE_TYPE = 0x0,
DDL_SHORT_TYPE = 0x1,
DDL_UINT_TYPE = 0x2,
DDL_INT_TYPE = 0x3,
DDL_UINT64_TYPE = 0x4,
DDL_FLOAT_TYPE = 0x5,
DDL_FIXEDPOINT_TYPE = 0x6,
DDL_STRING_TYPE = 0x7,
DDL_STRUCT_TYPE = 0x8,
DDL_ENUM_TYPE = 0x9,
};
union DDLValue
{
int intValue;
unsigned int uintValue;
unsigned __int64 uint64Value;
float floatValue;
float fixedPointValue;
const char* stringPtr;
};
struct DDLMember
{
const char* name;
int index;
int bitSize;
int limitSize;
int offset;
int type;
int externalIndex;
unsigned int rangeLimit;
bool isArray;
int arraySize;
int enumIndex;
};
struct DDLState
{
bool isValid;
int offset;
int arrayIndex;
DDLMember* member;
//const DDLDef* ddlDef;
};
struct DDLContext
{
};
}
using namespace ddl;
namespace session
{
struct SessionStaticData
{
const char* sessionName;
bool registerUsersWithVoice;
};
struct ClientInfo
{
bool registered;
bool voiceRegistered;
unsigned __int64 xuid;
int natType;
netadr_s addr;
int usrVoiceConnectivityBits;
int nextConnectivityTestTime[1];
bool muted;
bool privateSlot;
};
struct RegisteredUser
{
bool active;
unsigned __int64 xuid;
bool privateSlot;
};
struct SessionDynamicData
{
bool sessionHandle;
char __pad0[47];
bool keysGenerated;
bool sessionStartCalled;
unsigned __int64 sessionNonce;
int privateSlots;
int publicSlots;
int flags;
bool qosListenEnabled;
ClientInfo users[18];
int localVoiceConnectivityBits;
int sessionCreateController;
int sessionDeleteTime;
bool allowJoining;
RegisteredUser internalRegisteredUsers[18];
};
struct SessionData
{
SessionStaticData staticData;
SessionDynamicData dyn;
};
assert_sizeof(SessionData, 1552);
assert_offsetof(SessionData, dyn.users, 96);
assert_offsetof(SessionData, dyn.internalRegisteredUsers, 1120);
}
using namespace session;
namespace party
{
enum PartyPreloadMapStage : std::uint32_t
{
PRELOAD_MAP_IDLE = 0x0,
PRELOAD_MAP_INITIATED = 0x1,
PRELOAD_MAP_STARTED = 0x2,
PRELOAD_MAP_COUNT = 0x3,
};
struct PartyData
{
SessionData* session;
char __pad0[11436];
PartyPreloadMapStage preloadingMapStage;
char __pad1[101];
bool m_gameStartSkipCountdown;
char __pad2[110];
int lobbyFlags;
bool gameStartRequested;
};
static_assert(offsetof(PartyData, preloadingMapStage) == 11444);
static_assert(offsetof(PartyData, m_gameStartSkipCountdown) == 11549);
static_assert(offsetof(PartyData, lobbyFlags) == 11660);
static_assert(offsetof(PartyData, gameStartRequested) == 11664);
}
using namespace party;
namespace sv
{
struct SvServerInitSettings
{
char mapName[64];
char gameType[64];
char serverHostName[64];
bool hardcoreMode;
unsigned int maxClientCount;
unsigned int maxAgentCount;
bool isMapPreloaded;
bool isSaveGame;
bool isRestart;
bool isFrontEnd;
char __pad0[2];
bool serverThreadStartup;
}; //static_assert(sizeof(SvServerInitSettings) == 212);
static_assert(offsetof(SvServerInitSettings, maxClientCount) == 196);
static_assert(offsetof(SvServerInitSettings, isMapPreloaded) == 204);
static_assert(offsetof(SvServerInitSettings, isFrontEnd) == 207);
static_assert(offsetof(SvServerInitSettings, serverThreadStartup) == 210);
}
using namespace sv;
namespace pmem
{
enum PMem_Stack : __int32
{
PMEM_STACK_GAME = 0x0,
PMEM_STACK_RENDER_TARGETS = 0x1,
PMEM_STACK_MEM_VIRTUAL = 0x2,
PMEM_STACK_MEMCARD_LARGE_BUFFER = 0x3,
PMEM_STACK_SOUND = 0x4,
PMEM_STACK_STASHED_MEMORY = 0x5,
PMEM_STACK_CINEMATIC = 0x6,
PMEM_STACK_COUNT = 0x7,
};
enum PMem_Source
{
PMEM_SOURCE_EXTERNAL = 0x0,
PMEM_SOURCE_SCRIPT = 0x1,
};
enum PMem_Direction : __int32
{
PHYS_ALLOC_LOW = 0x0,
PHYS_ALLOC_HIGH = 0x1,
PHYS_ALLOC_COUNT = 0x2,
};
enum Mem_PageID
{
};
struct Mem_PageRange
{
Mem_PageID firstPageID;
Mem_PageID lastPageID;
};
struct PhysicalMemoryAllocation
{
const char* name;
char* prev_buffer;
char* next_buffer;
unsigned __int64 pos;
Mem_PageRange pageRange;
};
struct PhysicalMemoryPrim
{
const char* name;
unsigned int allocListCount;
char __pad0[4];
unsigned __int8* buf;
unsigned __int64 unk_pos;
int unk1;
char __pad2[4];
unsigned __int64 pos;
PhysicalMemoryAllocation allocList[32];
};
struct PhysicalMemory
{
PhysicalMemoryPrim prim[2];
};
}
using namespace pmem;
}

View File

@ -149,6 +149,7 @@ namespace game
WEAK symbol<void(int localClientNum)> LUI_CoD_CLoseAll{ 0x6135C0 };
WEAK symbol<unsigned int(int controllerIndex)> Live_SyncOnlineDataFlags{ 0xDC5CE0 };
WEAK symbol<std::uint64_t(int controllerIndex)> Live_GetXuid{ 0xD32A20 };
WEAK symbol<PartyData* ()> Lobby_GetPartyData{ 0x9C3E20 };
@ -163,6 +164,8 @@ namespace game
WEAK symbol<PartyData* ()> Party_GetActiveParty{ 0x9CC010 };
WEAK symbol<void(const unsigned int controllerIndex, XUID xuid)> PlayercardCache_AddToDownload{ 0xDB72E0 };
WEAK symbol<GfxFont* (const char* font, int size)> R_RegisterFont{ 0xDFC670 };
WEAK symbol<int(const char* text, int maxChars, GfxFont* font)> R_TextWidth{ 0xDFC770 };
WEAK symbol<int(void* font)> R_GetFontHeight{ 0x12727B0 };
@ -178,6 +181,11 @@ namespace game
#define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \
IW7_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, CP, CC, game::R_DrawSomething(S), 0, 0, 0, 0)
WEAK symbol<std::uint64_t(const void* session, const int clientNum)> Session_GetXuid{ 0xC72AB0 };
WEAK symbol<bool(const SessionData* session, const int memberIndex)> Session_IsHost{ 0xD9B470 };
WEAK symbol<int(const char* str, std::uint64_t* xuid)> StringToXUID{ 0xCE6C40 };
WEAK symbol<char* ()> Sys_Cwd{ 0xCFE5A0 };
WEAK symbol<int()> Sys_Milliseconds{ 0xD58110 };
@ -229,8 +237,9 @@ namespace game
WEAK symbol<void()> SV_CmdsSP_MapRestart_f{ 0xC12B30 };
WEAK symbol<void()> SV_CmdsSP_FastRestart_f{ 0xC12AF0 };
WEAK symbol<int (int clientNum)> SV_ClientMP_GetClientPing{ 0xC507D0 };
WEAK symbol<char* (int entNum)> SV_GameMP_GetGuid{ 0XC12410 };
WEAK symbol<char* (int entNum)> SV_GameMP_GetGuid{ 0xC12410 };
WEAK symbol<void()> SV_MainMP_KillLocalServer{ 0xC58DF0 };
WEAK symbol<SessionData* ()> SV_MainMP_GetServerLobby{ 0xC58DA0 };
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{ 0xC54780 };
WEAK symbol<void(client_t* drop, const char* reason, bool tellThem)> SV_DropClient{ 0xC4FBA0 };
WEAK symbol<bool()> SV_Loaded{ 0xC114C0 };
@ -258,6 +267,9 @@ namespace game
WEAK symbol<cmd_function_s*> cmd_functions{ 0x5D65CC8 };
WEAK symbol<const char*> command_whitelist{ 0x14D1B70 };
WEAK symbol<PLAYERCARD_CACHE_TASK_STAGE> g_DWPlayercardCacheDownloadTaskStage{ 0x80AE414 };
WEAK symbol<CachedPlayerProfile> cached_playercards{ 0x80AE420 };
WEAK symbol<GfxDrawMethod> gfxDrawMethod{ 0x83E86A8 };
WEAK symbol<int> keyCatchers{ 0x2246C34 };
@ -273,6 +285,8 @@ namespace game
WEAK symbol<unsigned int> svs_numclients{ 0x6B229E0 };
WEAK symbol<client_t*> svs_clients{ 0x6B22950 };
WEAK symbol<SessionData> g_serverSession{ 0x6B4E080 };
WEAK symbol<clientUIActive_t> clientUIActives{ 0x2246C30 };
WEAK symbol<connection_data*> cl_con_data{ 0x1FE58B8 };

View File

@ -1,9 +1,6 @@
#pragma once
#include <d3d11.h>
#define assert_sizeof(__ASSET__, __SIZE__) static_assert(sizeof(__ASSET__) == __SIZE__)
#define assert_offsetof(__ASSET__, __VARIABLE__, __OFFSET__) static_assert(offsetof(__ASSET__, __VARIABLE__) == __OFFSET__)
namespace game::database
{
typedef float vec_t;

View File

@ -1,58 +0,0 @@
#pragma once
namespace game::ddl
{
enum DDLType
{
DDL_INVALID_TYPE = 0xFFFFFFFF,
DDL_BYTE_TYPE = 0x0,
DDL_SHORT_TYPE = 0x1,
DDL_UINT_TYPE = 0x2,
DDL_INT_TYPE = 0x3,
DDL_UINT64_TYPE = 0x4,
DDL_FLOAT_TYPE = 0x5,
DDL_FIXEDPOINT_TYPE = 0x6,
DDL_STRING_TYPE = 0x7,
DDL_STRUCT_TYPE = 0x8,
DDL_ENUM_TYPE = 0x9,
};
union DDLValue
{
int intValue;
unsigned int uintValue;
unsigned __int64 uint64Value;
float floatValue;
float fixedPointValue;
const char* stringPtr;
};
struct DDLMember
{
const char* name;
int index;
int bitSize;
int limitSize;
int offset;
int type;
int externalIndex;
unsigned int rangeLimit;
bool isArray;
int arraySize;
int enumIndex;
};
struct DDLState
{
bool isValid;
int offset;
int arrayIndex;
DDLMember* member;
//const DDLDef* ddlDef;
};
struct DDLContext
{
};
}

View File

@ -1,27 +0,0 @@
#pragma once
namespace game::party
{
enum PartyPreloadMapStage : std::uint32_t
{
PRELOAD_MAP_IDLE = 0x0,
PRELOAD_MAP_INITIATED = 0x1,
PRELOAD_MAP_STARTED = 0x2,
PRELOAD_MAP_COUNT = 0x3,
};
struct PartyData
{
char __pad0[11444];
PartyPreloadMapStage preloadingMapStage;
char __pad1[101];
bool m_gameStartSkipCountdown;
char __pad2[110];
int lobbyFlags;
bool gameStartRequested;
};
static_assert(offsetof(PartyData, preloadingMapStage) == 11444);
static_assert(offsetof(PartyData, m_gameStartSkipCountdown) == 11549);
static_assert(offsetof(PartyData, lobbyFlags) == 11660);
static_assert(offsetof(PartyData, gameStartRequested) == 11664);
}

View File

@ -1,66 +0,0 @@
#pragma once
namespace game::pmem
{
enum PMem_Stack : __int32
{
PMEM_STACK_GAME = 0x0,
PMEM_STACK_RENDER_TARGETS = 0x1,
PMEM_STACK_MEM_VIRTUAL = 0x2,
PMEM_STACK_MEMCARD_LARGE_BUFFER = 0x3,
PMEM_STACK_SOUND = 0x4,
PMEM_STACK_STASHED_MEMORY = 0x5,
PMEM_STACK_CINEMATIC = 0x6,
PMEM_STACK_COUNT = 0x7,
};
enum PMem_Source
{
PMEM_SOURCE_EXTERNAL = 0x0,
PMEM_SOURCE_SCRIPT = 0x1,
};
enum PMem_Direction : __int32
{
PHYS_ALLOC_LOW = 0x0,
PHYS_ALLOC_HIGH = 0x1,
PHYS_ALLOC_COUNT = 0x2,
};
enum Mem_PageID
{
};
struct Mem_PageRange
{
Mem_PageID firstPageID;
Mem_PageID lastPageID;
};
struct PhysicalMemoryAllocation
{
const char* name;
char* prev_buffer;
char* next_buffer;
unsigned __int64 pos;
Mem_PageRange pageRange;
};
struct PhysicalMemoryPrim
{
const char* name;
unsigned int allocListCount;
char __pad0[4];
unsigned __int8* buf;
unsigned __int64 unk_pos;
int unk1;
char __pad2[4];
unsigned __int64 pos;
PhysicalMemoryAllocation allocList[32];
};
struct PhysicalMemory
{
PhysicalMemoryPrim prim[2];
};
}

View File

@ -1,24 +0,0 @@
#pragma once
namespace game::sv
{
struct SvServerInitSettings
{
char mapName[64];
char gameType[64];
char serverHostName[64];
bool hardcoreMode;
unsigned int maxClientCount;
unsigned int maxAgentCount;
bool isMapPreloaded;
bool isSaveGame;
bool isRestart;
bool isFrontEnd;
char __pad0[2];
bool serverThreadStartup;
}; //static_assert(sizeof(SvServerInitSettings) == 212);
static_assert(offsetof(SvServerInitSettings, maxClientCount) == 196);
static_assert(offsetof(SvServerInitSettings, isMapPreloaded) == 204);
static_assert(offsetof(SvServerInitSettings, isFrontEnd) == 207);
static_assert(offsetof(SvServerInitSettings, serverThreadStartup) == 210);
}

View File

@ -0,0 +1,162 @@
#include <std_include.hpp>
#include "fragment_handler.hpp"
namespace game::fragment_handler
{
namespace
{
constexpr size_t MAX_FRAGMENTS = 100;
using fragments = std::unordered_map<size_t, std::string>;
struct fragmented_packet
{
size_t fragment_count{0};
fragments fragments{};
std::chrono::high_resolution_clock::time_point insertion_time = std::chrono::high_resolution_clock::now();
};
using id_fragment_map = std::unordered_map<uint64_t, fragmented_packet>;
using address_fragment_map = std::unordered_map<netadr_s, id_fragment_map>;
utils::concurrency::container<address_fragment_map> global_map{};
std::vector<std::string> construct_fragments(const void* data, const size_t length)
{
std::vector<std::string> fragments{};
constexpr size_t max_fragment_size = 0x400;
for (size_t i = 0; i < length; i += max_fragment_size)
{
const auto current_fragment_size = std::min(length - i, max_fragment_size);
std::string fragment(static_cast<const char*>(data) + i, current_fragment_size);
fragments.push_back(std::move(fragment));
}
return fragments;
}
}
bool handle(const netadr_s& target, utils::byte_buffer& buffer, std::string& final_packet)
{
const auto fragment_id = buffer.read<uint64_t>();
const size_t fragment_count = buffer.read<uint32_t>();
const size_t fragment_index = buffer.read<uint32_t>();
auto fragment_data = buffer.get_remaining_data();
// Check for valid fragment_count and fragment_index
if (fragment_count == 0 || fragment_count > MAX_FRAGMENTS || fragment_index >= fragment_count)
{
return false;
}
return global_map.access<bool>([&](address_fragment_map& map)
{
auto& user_map = map[target];
// Check if the user_map is full
if (!user_map.contains(fragment_id) && user_map.size() > MAX_FRAGMENTS)
{
return false;
}
auto& packet_queue = user_map[fragment_id];
// Set fragment_count if not set
if (packet_queue.fragment_count == 0)
{
packet_queue.fragment_count = fragment_count;
}
// Check if fragment_count matches
if (packet_queue.fragment_count != fragment_count)
{
return false;
}
// Ensure fragment_index is within bounds
if (packet_queue.fragments.size() + 1 < fragment_count)
{
packet_queue.fragments[fragment_index] = std::move(fragment_data);
return false;
}
final_packet.clear();
// Reconstruct final_packet
for (size_t i = 0; i < fragment_count; ++i)
{
if (i == fragment_index)
{
final_packet.append(fragment_data.data(), fragment_data.size());
}
else
{
final_packet.append(packet_queue.fragments[i].data(), packet_queue.fragments[i].size());
}
}
return true;
});
}
void clean()
{
global_map.access([](address_fragment_map& map)
{
for (auto i = map.begin(); i != map.end();)
{
auto& user_map = i->second;
for (auto j = user_map.begin(); j != user_map.end();)
{
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - j->second.insertion_time;
if (diff > 5s)
{
j = user_map.erase(j);
}
else
{
++j;
}
}
if (user_map.empty())
{
i = map.erase(i);
}
else
{
++i;
}
}
});
}
void fragment_handler::fragment_data(const void* data, const size_t size,
const std::function<void(const utils::byte_buffer& buffer)>& callback)
{
static std::atomic_uint64_t current_id{0};
const auto id = current_id++;
const auto fragments = construct_fragments(data, size);
for (size_t i = 0; i < fragments.size(); ++i)
{
utils::byte_buffer buffer{};
buffer.write(id);
buffer.write(static_cast<uint32_t>(fragments.size()));
buffer.write(static_cast<uint32_t>(i));
auto& fragment = fragments.at(i);
buffer.write(fragment.data(), fragment.size());
callback(buffer);
}
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <utils/byte_buffer.hpp>
#include <utils/concurrency.hpp>
#include "../../component/network.hpp"
namespace game::fragment_handler
{
bool handle(const netadr_s& target, utils::byte_buffer& buffer,
std::string& final_packet);
void clean();
void fragment_data(const void* data, const size_t size,
const std::function<void(const utils::byte_buffer& buffer)>& callback);
}

View File

@ -102,4 +102,7 @@
using namespace std::literals;
#define assert_sizeof(__ASSET__, __SIZE__) static_assert(sizeof(__ASSET__) == __SIZE__)
#define assert_offsetof(__ASSET__, __VARIABLE__, __OFFSET__) static_assert(offsetof(__ASSET__, __VARIABLE__) == __OFFSET__)
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

View File

@ -0,0 +1,53 @@
#include "byte_buffer.hpp"
#include <cstring>
namespace utils
{
byte_buffer::byte_buffer()
: writing_(true)
{
}
byte_buffer::byte_buffer(std::string buffer)
: writing_(false)
, buffer_(std::move(buffer))
{
}
void byte_buffer::write(const void* buffer, const size_t length)
{
if (!this->writing_)
{
throw std::runtime_error("Writing to readable byte buffer");
}
this->buffer_.append(static_cast<const char*>(buffer), length);
}
void byte_buffer::read(void* data, const size_t length)
{
if (this->writing_)
{
throw std::runtime_error("Reading from writable byte buffer");
}
if (this->offset_ + length > this->buffer_.size())
{
throw std::runtime_error("Out of bounds read from byte buffer");
}
memcpy(data, this->buffer_.data() + this->offset_, length);
this->offset_ += length;
}
std::string byte_buffer::read_data(const size_t length)
{
std::string result{};
result.resize(length);
this->read(result.data(), result.size());
return result;
}
}

View File

@ -0,0 +1,141 @@
#pragma once
#include <string>
#include <vector>
#include <stdexcept>
namespace utils
{
class byte_buffer
{
public:
byte_buffer();
byte_buffer(std::string buffer);
template <typename T>
byte_buffer(const std::basic_string_view<T>& buffer)
: byte_buffer(std::string(reinterpret_cast<const char*>(buffer.data()), buffer.size() * sizeof(T)))
{
}
void write(const void* buffer, size_t length);
void write(const char* text)
{
this->write(text, strlen(text));
}
void write_string(const char* str, const size_t length)
{
this->write<uint32_t>(static_cast<uint32_t>(length));
this->write(str, length);
}
void write_string(const std::string& str)
{
this->write_string(str.data(), str.size());
}
void write_string(const char* str)
{
this->write_string(str, strlen(str));
}
template <typename T>
void write(const T& object)
{
this->write(&object, sizeof(object));
}
template<>
void write<byte_buffer>(const byte_buffer& object)
{
const auto& buffer = object.get_buffer();
this->write(buffer.data(), buffer.size());
}
template <typename T>
void write(const std::vector<T>& vec)
{
this->write(vec.data(), vec.size() * sizeof(T));
}
template <typename T>
void write_vector(const std::vector<T>& vec)
{
this->write(static_cast<uint32_t>(vec.size()));
this->write(vec);
}
const std::string& get_buffer() const
{
return this->buffer_;
}
std::string move_buffer()
{
return std::move(this->buffer_);
}
void read(void* data, size_t length);
template <typename T>
T read()
{
T object{};
this->read(&object, sizeof(object));
return object;
}
template <typename T>
std::vector<T> read_vector()
{
std::vector<T> result{};
const auto size = this->read<uint32_t>();
const auto totalSize = size * sizeof(T);
if (this->offset_ + totalSize > this->buffer_.size())
{
throw std::runtime_error("Out of bounds read from byte buffer");
}
result.resize(size);
this->read(result.data(), totalSize);
return result;
}
std::string read_string()
{
std::string result{};
const auto size = this->read<uint32_t>();
if (this->offset_ + size > this->buffer_.size())
{
throw std::runtime_error("Out of bounds read from byte buffer");
}
result.resize(size);
this->read(result.data(), size);
return result;
}
size_t get_remaining_size() const
{
return this->buffer_.size() - offset_;
}
std::string get_remaining_data()
{
return this->read_data(this->get_remaining_size());
}
std::string read_data(size_t length);
private:
bool writing_{false};
size_t offset_{0};
std::string buffer_{};
};
}

View File

@ -39,6 +39,11 @@ namespace utils::concurrency
T& get_raw() { return object_; }
const T& get_raw() const { return object_; }
std::unique_lock<MutexType> accquire_lock()
{
return std::unique_lock<MutexType>{mutex_};
}
private:
mutable MutexType mutex_{};
T object_{};