Prepare overwriting the player name

Step 1 for #47
This commit is contained in:
Maurice Heumann 2023-03-07 19:23:08 +01:00
parent 73f9859eed
commit 81d4cc47b4
12 changed files with 387 additions and 5 deletions

View File

@ -0,0 +1,103 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "name.hpp"
#include "steam_proxy.hpp"
#include "command.hpp"
#include <utils/nt.hpp>
#include <utils/string.hpp>
#include <utils/properties.hpp>
#include <utils/concurrency.hpp>
namespace name
{
namespace
{
utils::concurrency::container<std::string> player_name{};
void store_player_name(const std::string& name)
{
utils::properties::store("playerName", name);
}
void activate_player_name(std::string new_name)
{
player_name.access([&](std::string& name)
{
name = std::move(new_name);
});
}
void update_player_name(const std::string& new_name)
{
store_player_name(new_name);
activate_player_name(new_name);
}
void setup_player_name()
{
std::string initial_name = steam_proxy::get_player_name();
if (initial_name.empty())
{
initial_name = utils::nt::get_user_name();
}
if (initial_name.empty())
{
initial_name = "Unknown Soldier";
}
update_player_name(initial_name);
}
void load_player_name()
{
const auto stored_name = utils::properties::load("playerName");
if (stored_name)
{
activate_player_name(*stored_name);
}
else
{
setup_player_name();
}
}
}
struct component final : client_component
{
void post_load() override
{
load_player_name();
}
void post_unpack() override
{
command::add("name", [](const command::params& params)
{
if (params.size() != 2)
{
return;
}
update_player_name(params[1]);
});
}
component_priority priority() const override
{
return component_priority::name;
}
};
const char* get_player_name()
{
const auto name = player_name.copy();
return utils::string::va("%.*s", static_cast<int>(name.size()), name.data());
}
}
REGISTER_COMPONENT(name::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace name
{
const char* get_player_name();
}

View File

@ -7,6 +7,8 @@
#include <utils/finally.hpp> #include <utils/finally.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
#include "game/utils.hpp"
#include "steam/interface.hpp" #include "steam/interface.hpp"
#include "steam/steam.hpp" #include "steam/steam.hpp"
@ -252,7 +254,7 @@ namespace steam_proxy
return client_friends.invoke<const char*>("GetPersonaName"); return client_friends.invoke<const char*>("GetPersonaName");
} }
return "boiii"; return "";
} }
void update_subscribed_items() void update_subscribed_items()

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <utils/nt.hpp>
namespace steam_proxy namespace steam_proxy
{ {
const utils::nt::library& get_overlay_module(); const utils::nt::library& get_overlay_module();

View File

@ -3,6 +3,8 @@
enum class component_priority enum class component_priority
{ {
min = 0, min = 0,
// must run after the steam_proxy
name,
// must run after the updater // must run after the updater
steam_proxy, steam_proxy,
updater, updater,

View File

@ -3,13 +3,13 @@
#include <utils/nt.hpp> #include <utils/nt.hpp>
#include "component/steam_proxy.hpp" #include "component/name.hpp"
namespace steam namespace steam
{ {
const char* friends::GetPersonaName() const char* friends::GetPersonaName()
{ {
return steam_proxy::get_player_name(); return name::get_player_name();
} }
unsigned long long friends::SetPersonaName(const char* pchPersonaName) unsigned long long friends::SetPersonaName(const char* pchPersonaName)

View File

@ -380,13 +380,13 @@ namespace updater
legal_files.reserve(files.size()); legal_files.reserve(files.size());
for (const auto& file : files) for (const auto& file : files)
{ {
if (file.name != UPDATE_HOST_BINARY) if (file.name.starts_with("data"))
{ {
legal_files.emplace_back(std::filesystem::absolute(base / file.name)); legal_files.emplace_back(std::filesystem::absolute(base / file.name));
} }
} }
const auto existing_files = utils::io::list_files(base.string(), true); const auto existing_files = utils::io::list_files(base / "data", true);
for (auto& file : existing_files) for (auto& file : existing_files)
{ {
const auto is_file = std::filesystem::is_regular_file(file); const auto is_file = std::filesystem::is_regular_file(file);

View File

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

View File

@ -0,0 +1,44 @@
#include "named_mutex.hpp"
#include "nt.hpp"
namespace utils
{
named_mutex::named_mutex(const std::string& name)
{
this->handle_ = CreateMutexA(nullptr, FALSE, name.data());
}
named_mutex::~named_mutex()
{
if (this->handle_)
{
CloseHandle(this->handle_);
}
}
void named_mutex::lock() const
{
if (this->handle_)
{
WaitForSingleObject(this->handle_, INFINITE);
}
}
bool named_mutex::try_lock(const std::chrono::milliseconds timeout) const
{
if (this->handle_)
{
return WAIT_OBJECT_0 == WaitForSingleObject(this->handle_, static_cast<DWORD>(timeout.count()));
}
return false;
}
void named_mutex::unlock() const noexcept
{
if (this->handle_)
{
ReleaseMutex(this->handle_);
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <string>
#include <chrono>
namespace utils
{
class named_mutex
{
public:
named_mutex(const std::string& name);
~named_mutex();
named_mutex(named_mutex&&) = delete;
named_mutex(const named_mutex&) = delete;
named_mutex& operator=(named_mutex&&) = delete;
named_mutex& operator=(const named_mutex&) = delete;
void lock() const;
bool try_lock(std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) const;
void unlock() const noexcept;
private:
void* handle_{};
};
}

View File

@ -0,0 +1,172 @@
#include "properties.hpp"
#include "finally.hpp"
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h"
#include "io.hpp"
#include "com.hpp"
#include "string.hpp"
namespace utils::properties
{
namespace
{
typedef rapidjson::GenericDocument<rapidjson::UTF16LE<>> WDocument;
typedef rapidjson::GenericValue<rapidjson::UTF16LE<>> WValue;
typedef rapidjson::EncodedOutputStream<rapidjson::UTF16LE<>, rapidjson::FileWriteStream> OutputStream;
typedef rapidjson::EncodedInputStream<rapidjson::UTF16LE<>, rapidjson::FileReadStream> InputStream;
std::filesystem::path get_properties_folder()
{
static auto props = get_appdata_path() / "user";
return props;
}
std::filesystem::path get_properties_file()
{
static auto props = get_properties_folder() / "properties.json";
return props;
}
WDocument load_properties()
{
WDocument default_doc{};
default_doc.SetObject();
char read_buffer[256]; // Raw buffer for reading
const std::wstring& props = get_properties_file();
FILE* fp;
auto err = _wfopen_s(&fp, props.data(), L"rb");
if (err || !fp)
{
return default_doc;
}
// This will handle the BOM
rapidjson::FileReadStream bis(fp, read_buffer, sizeof(read_buffer));
InputStream eis(bis);
WDocument doc{};
const rapidjson::ParseResult result = doc.ParseStream<rapidjson::kParseNoFlags, rapidjson::UTF16LE<>>(eis);
fclose(fp);
if (!result || !doc.IsObject())
{
return default_doc;
}
return doc;
}
void store_properties(const WDocument& doc)
{
char write_buffer[256]; // Raw buffer for writing
const std::wstring& props = get_properties_file();
io::create_directory(get_properties_folder());
FILE* fp;
auto err = _wfopen_s(&fp, props.data(), L"wb");
if (err || !fp)
{
return;
}
rapidjson::FileWriteStream bos(fp, write_buffer, sizeof(write_buffer));
OutputStream eos(bos, true); // Write BOM
rapidjson::Writer<OutputStream, rapidjson::UTF16LE<>, rapidjson::UTF16LE<>> writer(eos);
doc.Accept(writer);
fclose(fp);
}
}
std::filesystem::path get_appdata_path()
{
PWSTR path;
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
{
throw std::runtime_error("Failed to read APPDATA path!");
}
auto _ = utils::finally([&path]
{
CoTaskMemFree(path);
});
static auto appdata = std::filesystem::path(path) / "boiii";
return appdata;
}
std::unique_lock<named_mutex> lock()
{
static named_mutex mutex{"boiii-properties-lock"};
return std::unique_lock{mutex};
}
std::optional<std::wstring> load(const std::wstring& name)
{
const auto _ = lock();
const auto doc = load_properties();
if (!doc.HasMember(name))
{
return {};
}
const auto& value = doc[name];
if (!value.IsString())
{
return {};
}
return {std::wstring{value.GetString()}};
}
std::optional<std::string> load(const std::string& name)
{
const auto result = load(string::convert(name));
if (!result)
{
return {};
}
return {string::convert(*result)};
}
void store(const std::wstring& name, const std::wstring& value)
{
const auto _ = lock();
auto doc = load_properties();
while (doc.HasMember(name))
{
doc.RemoveMember(name);
}
WValue key{};
key.SetString(name, doc.GetAllocator());
WValue member{};
member.SetString(value, doc.GetAllocator());
doc.AddMember(key, member, doc.GetAllocator());
store_properties(doc);
}
void store(const std::string& name, const std::string& value)
{
store(string::convert(name), string::convert(value));
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "named_mutex.hpp"
#include <mutex>
#include <optional>
#include <filesystem>
namespace utils::properties
{
std::filesystem::path get_appdata_path();
std::unique_lock<named_mutex> lock();
std::optional<std::wstring> load(const std::wstring& name);
std::optional<std::string> load(const std::string& name);
void store(const std::wstring& name, const std::wstring& value);
void store(const std::string& name, const std::string& value);
}