From 3a1a474c7c2efbadeb41f5b2b73f7f860f2b59fa Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 26 May 2016 23:52:40 +0100 Subject: [PATCH] refactor key map to User and UserManager classes --- openrct2.vcxproj | 2 + src/core/Nullable.hpp | 6 + src/network/NetworkUser.cpp | 222 ++++++++++++++++++++++++++++++++++ src/network/NetworkUser.h | 65 ++++++++++ src/network/network.cpp | 232 +++++++----------------------------- src/network/network.h | 12 +- 6 files changed, 342 insertions(+), 197 deletions(-) create mode 100644 src/network/NetworkUser.cpp create mode 100644 src/network/NetworkUser.h diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 5952fcceda..f22ed97908 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -84,6 +84,7 @@ + @@ -357,6 +358,7 @@ + diff --git a/src/core/Nullable.hpp b/src/core/Nullable.hpp index 37e3393cfe..67ba9b2c51 100644 --- a/src/core/Nullable.hpp +++ b/src/core/Nullable.hpp @@ -28,6 +28,12 @@ public: _hasValue = false; } + Nullable(nullptr_t) + { + _value = T(); + _hasValue = false; + } + Nullable(const T &value) { _value = value; diff --git a/src/network/NetworkUser.cpp b/src/network/NetworkUser.cpp new file mode 100644 index 0000000000..2966fe0a8d --- /dev/null +++ b/src/network/NetworkUser.cpp @@ -0,0 +1,222 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include + +#include "../core/Json.hpp" +#include "../core/Path.hpp" +#include "../core/String.hpp" +#include "NetworkUser.h" + +extern "C" +{ + #include "../platform/platform.h" +} + +constexpr const utf8 * USER_STORE_FILENAME = "users.json"; + +NetworkUser * NetworkUser::FromJson(json_t * json) +{ + const char * hash = json_string_value(json_object_get(json, "hash")); + const char * name = json_string_value(json_object_get(json, "name")); + const json_t * jsonGroupId = json_object_get(json, "groupId"); + + NetworkUser * user = nullptr; + if (hash != nullptr && name != nullptr) { + user = new NetworkUser(); + user->Hash = std::string(hash); + user->Name = std::string(name); + if (!json_is_null(jsonGroupId)) { + user->GroupId = (uint8)json_integer_value(jsonGroupId); + } + return user; + } + return user; +} + +json_t * NetworkUser::ToJson() const +{ + return ToJson(json_object()); +} + +json_t * NetworkUser::ToJson(json_t * json) const +{ + json_object_set_new(json, "hash", json_string(Hash.c_str())); + json_object_set_new(json, "name", json_string(Name.c_str())); + + json_t * jsonGroupId; + if (GroupId.HasValue()) { + jsonGroupId = json_integer(GroupId.GetValue()); + } else { + jsonGroupId = json_null(); + } + json_object_set_new(json, "groupId", jsonGroupId); + + return json; +} + +NetworkUserManager::~NetworkUserManager() +{ + DisposeUsers(); +} + +void NetworkUserManager::DisposeUsers() +{ + for (const auto &kvp : _usersByHash) + { + delete kvp.second; + } + _usersByHash.clear(); +} + +void NetworkUserManager::Load() +{ + utf8 path[MAX_PATH]; + GetStorePath(path, sizeof(path)); + + if (platform_file_exists(path)) + { + DisposeUsers(); + + json_t * jsonUsers = Json::ReadFromFile(path); + size_t numUsers = json_array_size(jsonUsers); + for (size_t i = 0; i < numUsers; i++) + { + json_t * jsonUser = json_array_get(jsonUsers, i); + NetworkUser * networkUser = NetworkUser::FromJson(jsonUser); + if (networkUser != nullptr) + { + _usersByHash[networkUser->Hash] = networkUser; + } + } + json_decref(jsonUsers); + } +} + +void NetworkUserManager::Save() +{ + utf8 path[MAX_PATH]; + GetStorePath(path, sizeof(path)); + + json_t * jsonUsers = nullptr; + try + { + if (platform_file_exists(path)) + { + jsonUsers = Json::ReadFromFile(path); + } + } + catch (Exception) { } + + if (jsonUsers == nullptr) + { + jsonUsers = json_array(); + } + + // Update existing users + std::unordered_set savedHashes; + size_t numUsers = json_array_size(jsonUsers); + for (size_t i = 0; i < numUsers; i++) + { + json_t * jsonUser = json_array_get(jsonUsers, i); + const char * hash = json_string_value(json_object_get(jsonUser, "hash")); + if (hash != nullptr) + { + auto hashString = std::string(hash); + const NetworkUser * networkUser = GetUserByHash(hashString); + networkUser->ToJson(jsonUser); + savedHashes.insert(hashString); + } + } + + // Add new users + for (const auto &kvp : _usersByHash) + { + const NetworkUser * networkUser = kvp.second; + if (savedHashes.find(networkUser->Hash) == savedHashes.end()) + { + json_t * jsonUser = networkUser->ToJson(); + json_array_append_new(jsonUsers, jsonUser); + } + } + + Json::WriteToFile(path, jsonUsers, JSON_INDENT(4) | JSON_PRESERVE_ORDER); + json_decref(jsonUsers); +} + +void NetworkUserManager::UnsetUsersOfGroup(uint8 groupId) +{ + for (const auto &kvp : _usersByHash) + { + NetworkUser * networkUser = kvp.second; + if (networkUser->GroupId.HasValue() && + networkUser->GroupId.GetValue() == groupId) + { + networkUser->GroupId = nullptr; + } + } +} + +NetworkUser * NetworkUserManager::GetUserByHash(const std::string &hash) +{ + auto it = _usersByHash.find(hash); + if (it != _usersByHash.end()) + { + return it->second; + } + return nullptr; +} + +const NetworkUser * NetworkUserManager::GetUserByHash(const std::string &hash) const +{ + auto it = _usersByHash.find(hash); + if (it != _usersByHash.end()) + { + return it->second; + } + return nullptr; +} + +const NetworkUser * NetworkUserManager::GetUserByName(const std::string &name) const +{ + for (auto kvp : _usersByHash) + { + const NetworkUser * networkUser = kvp.second; + if (String::Equals(name.c_str(), networkUser->Name.c_str(), true)) + { + return networkUser; + } + } + return nullptr; +} + +NetworkUser * NetworkUserManager::GetOrAddUser(const std::string &hash) +{ + NetworkUser * networkUser = GetUserByHash(hash); + if (networkUser == nullptr) + { + networkUser = new NetworkUser(); + networkUser->Hash = hash; + _usersByHash[hash] = networkUser; + } + return networkUser; +} + +void NetworkUserManager::GetStorePath(utf8 * buffer, size_t bufferSize) +{ + platform_get_user_directory(buffer, nullptr); + Path::Append(buffer, bufferSize, USER_STORE_FILENAME); +} diff --git a/src/network/NetworkUser.h b/src/network/NetworkUser.h new file mode 100644 index 0000000000..f0983cceda --- /dev/null +++ b/src/network/NetworkUser.h @@ -0,0 +1,65 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "../common.h" +#include "../core/Nullable.hpp" + +#include +#include +#include + +class NetworkUser +{ +public: + std::string Hash; + std::string Name; + Nullable GroupId; + + static NetworkUser * FromJson(json_t * json); + + json_t * ToJson() const; + json_t * ToJson(json_t * json) const; +}; + +class NetworkUserManager +{ +public: + ~NetworkUserManager(); + + void Load(); + + /** + * @brief NetworkUserManager::Save + * Reads mappings from JSON, updates them in-place and saves JSON. + * + * Useful for retaining custom entries in JSON file. + */ + void Save(); + + void UnsetUsersOfGroup(uint8 groupId); + NetworkUser * GetUserByHash(const std::string &hash); + const NetworkUser * GetUserByHash(const std::string &hash) const; + const NetworkUser * GetUserByName(const std::string &name) const; + NetworkUser * GetOrAddUser(const std::string &hash); + +private: + std::map _usersByHash; + + void DisposeUsers(); + static void GetStorePath(utf8 * buffer, size_t bufferSize); +}; diff --git a/src/network/network.cpp b/src/network/network.cpp index 248e4d6e07..375cc29b71 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -774,7 +774,7 @@ bool Network::BeginServer(unsigned short port, const char* address) if (!Init()) return false; - LoadKeyMappings(); + _userManager.Load(); NetworkAddress networkaddress; networkaddress.Resolve(address, port, false); @@ -1312,23 +1312,22 @@ void Network::RemoveGroup(uint8 id) if (group != group_list.end()) { group_list.erase(group); } - for (auto &mapping : key_group_map) { - if (mapping.second.GroupId.HasValue() && mapping.second.GroupId.GetValue() == id) { - mapping.second.GroupId = Nullable(); - } + + if (GetMode() == NETWORK_MODE_SERVER) { + _userManager.UnsetUsersOfGroup(id); + _userManager.Save(); } - UpdateKeyMappings(); } uint8 Network::GetGroupIDByHash(const std::string &keyhash) { - auto it = key_group_map.find(keyhash); - if (it != key_group_map.end()) { - auto groupId = it->second.GroupId; - return groupId.GetValueOrDefault(GetDefaultGroup()); - } else { - return GetDefaultGroup(); + const NetworkUser * networkUser = _userManager.GetUserByHash(keyhash); + + uint8 groupId = GetDefaultGroup(); + if (networkUser != nullptr && networkUser->GroupId.HasValue()) { + groupId = networkUser->GroupId.GetValue(); } + return groupId; } uint8 Network::GetDefaultGroup() @@ -1426,153 +1425,6 @@ void Network::LoadGroups() json_decref(json); } -void Network::SaveKeyMappings() -{ - if (GetMode() == NETWORK_MODE_SERVER) { - utf8 path[MAX_PATH]; - network_get_keymap_path(path, sizeof(path)); - - json_t * jsonKeyMappings = json_array(); - for (auto it = key_group_map.cbegin(); it != key_group_map.cend(); it++) { - json_t *keyMapping = json_object(); - json_object_set_new(keyMapping, "hash", json_string(it->first.c_str())); - - json_t *jsonGroupId; - if (it->second.GroupId.HasValue()) { - jsonGroupId = json_integer(it->second.GroupId.GetValue()); - } else { - jsonGroupId = json_null(); - } - json_object_set_new(keyMapping, "groupId", jsonGroupId); - - json_array_append_new(jsonKeyMappings, keyMapping); - } - bool result; - try - { - Json::WriteToFile(path, jsonKeyMappings, JSON_INDENT(4) | JSON_PRESERVE_ORDER); - result = true; - } - catch (Exception ex) - { - log_error("Unable to save %s: %s", path, ex.GetMessage()); - result = false; - } - - json_decref(jsonKeyMappings); - } -} - -void Network::LoadKeyMappings() -{ - utf8 path[MAX_PATH]; - network_get_keymap_path(path, sizeof(path)); - - if (!platform_file_exists(path)) { - return; - } - key_group_map.clear(); - - json_t * jsonKeyMappings = Json::ReadFromFile(path); - - size_t groupCount = (size_t)json_array_size(jsonKeyMappings); - for (size_t i = 0; i < groupCount; i++) { - json_t * jsonKeyMapping = json_array_get(jsonKeyMappings, i); - - const char *hash = json_string_value(json_object_get(jsonKeyMapping, "hash")); - const char *name = json_string_value(json_object_get(jsonKeyMapping, "name")); - const json_t *jsonGroupId = json_object_get(jsonKeyMapping, "groupId"); - if (hash != nullptr && name != nullptr) { - KeyMapping keyMapping; - keyMapping.Hash = std::string(hash); - keyMapping.Name = std::string(name); - if (!json_is_null(jsonGroupId)) { - keyMapping.GroupId = (uint8)json_integer_value(jsonGroupId); - } - key_group_map[keyMapping.Hash] = keyMapping; - } - } - json_decref(jsonKeyMappings); -} - - -/** - * @brief Network::UpdateKeyMappings - * Reads mappings from JSON, updates them in-place and saves JSON. - * - * Useful for retaining custom entries in JSON file. - */ -void Network::UpdateKeyMappings() -{ - if (GetMode() != NETWORK_MODE_SERVER) { - return; - } - utf8 path[MAX_PATH]; - - platform_get_user_directory(path, NULL); - strcat(path, "keymappings.json"); - - auto local_key_map = std::map(key_group_map); - - json_t * jsonKeyMappings; - if (platform_file_exists(path)) { - jsonKeyMappings = Json::ReadFromFile(path); - - // Update all the existing entries in JSON - size_t groupCount = (size_t)json_array_size(jsonKeyMappings); - for (size_t i = 0; i < groupCount; i++) { - json_t * jsonKeyMapping = json_array_get(jsonKeyMappings, i); - std::string hash(json_string_value(json_object_get(jsonKeyMapping, "hash"))); - decltype(local_key_map.begin()) it; - if ((it = local_key_map.find(hash)) != local_key_map.end()) { - json_t *jsonGroupId; - if (it->second.GroupId.HasValue()) { - jsonGroupId = json_integer(it->second.GroupId.GetValue()); - } else { - jsonGroupId = json_null(); - } - json_object_set_new(jsonKeyMapping, "groupId", jsonGroupId); - - // remove item once it was found and set - local_key_map.erase(it); - } - } - } else { - jsonKeyMappings = json_array(); - } - - // Store remaining entries - for (auto it = local_key_map.cbegin(); it != local_key_map.cend(); it++) { - json_t *keyMapping = json_object(); - json_object_set(keyMapping, "hash", json_string(it->first.c_str())); - json_object_set(keyMapping, "name", json_string(it->second.Name.c_str())); - - json_t *jsonGroupId; - if (it->second.GroupId.HasValue()) { - jsonGroupId = json_integer(it->second.GroupId.GetValue()); - } else { - jsonGroupId = json_null(); - } - json_object_set(keyMapping, "groupId", jsonGroupId); - - json_array_append(jsonKeyMappings, keyMapping); - } - - bool result; - try - { - Json::WriteToFile(path, jsonKeyMappings, JSON_INDENT(4) | JSON_PRESERVE_ORDER); - result = true; - } - catch (Exception ex) - { - log_error("Unable to save %s: %s", path, ex.GetMessage()); - result = false; - } - - json_decref(jsonKeyMappings); -} - void Network::Client_Send_TOKEN() { log_verbose("requesting token"); @@ -1958,29 +1810,31 @@ NetworkPlayer* Network::AddPlayer(const utf8 *name, const std::string &keyhash) newid = 0; } if (newid != -1) { - // Load keys host may have added manually - LoadKeyMappings(); + std::unique_ptr player; + if (GetMode() == NETWORK_MODE_SERVER) { + // Load keys host may have added manually + _userManager.Load(); - // Check if the key is registered - const KeyMapping *keyMapping = nullptr; - { - auto it = key_group_map.find(keyhash); - if (it != key_group_map.end()) { - keyMapping = &it->second; - } - } + // Check if the key is registered + const NetworkUser * networkUser = _userManager.GetUserByHash(keyhash); - std::unique_ptr player(new NetworkPlayer); // change to make_unique in c++14 - player->id = newid; - player->keyhash = keyhash; - if (keyMapping == nullptr) { - player->group = GetDefaultGroup(); - if (!String::IsNullOrEmpty(name)) { - player->SetName(MakePlayerNameUnique(std::string(name))); + player = std::unique_ptr(new NetworkPlayer); // change to make_unique in c++14 + player->id = newid; + player->keyhash = keyhash; + if (networkUser == nullptr) { + player->group = GetDefaultGroup(); + if (!String::IsNullOrEmpty(name)) { + player->SetName(MakePlayerNameUnique(std::string(name))); + } + } else { + player->group = networkUser->GroupId.GetValueOrDefault(GetDefaultGroup()); + player->SetName(networkUser->Name); } } else { - player->group = keyMapping->GroupId.GetValueOrDefault(GetDefaultGroup()); - player->SetName(keyMapping->Name); + player = std::unique_ptr(new NetworkPlayer); // change to make_unique in c++14 + player->id = newid; + player->group = GetDefaultGroup(); + player->SetName(name); } addedplayer = player.get(); @@ -2009,11 +1863,8 @@ std::string Network::MakePlayerNameUnique(const std::string &name) if (unique) { // Check if there is already a registered player with this name - for (const auto &keyMapping : this->key_group_map) { - if (String::Equals(keyMapping.second.Name.c_str(), new_name.c_str(), true)) { - unique = false; - break; - } + if (_userManager.GetUserByName(new_name) != nullptr) { + unique = false; } } @@ -2168,7 +2019,7 @@ void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& p connection.authstatus = NETWORK_AUTH_VERIFICATIONFAILURE; log_verbose("Signature verification failed!"); } - if (gConfigNetwork.known_keys_only && key_group_map.find(hash) == key_group_map.end()) { + if (gConfigNetwork.known_keys_only && _userManager.GetUserByHash(hash) == nullptr) { connection.authstatus = NETWORK_AUTH_UNKNOWN_KEY_DISALLOWED; } } @@ -2702,9 +2553,16 @@ void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* } if (*ebx & GAME_COMMAND_FLAG_APPLY) { player->group = groupid; - gNetwork.key_group_map[player->keyhash].GroupId = groupid; - gNetwork.key_group_map[player->keyhash].Name = player->name; - gNetwork.UpdateKeyMappings(); + + if (network_get_mode() == NETWORK_MODE_SERVER) { + // Add or update saved user + NetworkUserManager *userManager = &gNetwork._userManager; + NetworkUser *networkUser = userManager->GetOrAddUser(player->keyhash); + networkUser->GroupId = groupid; + networkUser->Name = player->name; + userManager->Save(); + } + window_invalidate_by_number(WC_PLAYER, playerid); } *ebx = 0; diff --git a/src/network/network.h b/src/network/network.h index 07397fefc7..c506d1e2cb 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -124,6 +124,7 @@ extern "C" { #include "../core/Json.hpp" #include "../core/Nullable.hpp" #include "NetworkKey.h" +#include "NetworkUser.h" template struct ByteSwapT { }; @@ -341,12 +342,6 @@ private: std::shared_ptr status; }; -struct KeyMapping { - std::string Hash; - std::string Name; - Nullable GroupId; -}; - class Network { public: @@ -381,9 +376,6 @@ public: void SetDefaultGroup(uint8 id); void SaveGroups(); void LoadGroups(); - void SaveKeyMappings(); - void LoadKeyMappings(); - void UpdateKeyMappings(); void Client_Send_TOKEN(); void Client_Send_AUTH(const char* name, const char* password, const char *pubkey, const char *sig, size_t sigsize); @@ -411,7 +403,7 @@ public: std::vector> group_list; NetworkKey key; std::vector challenge; - std::map key_group_map; + NetworkUserManager _userManager; private: bool ProcessConnection(NetworkConnection& connection);