/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * 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 General Public License along * with this program. If not, see . */ #include "RealmList.h" #include "DatabaseEnv.h" #include "Log.h" #include "QueryResult.h" #include "Resolver.h" #include "SteadyTimer.h" #include "Util.h" #include #include RealmList::RealmList() : _updateInterval(0) { } RealmList* RealmList::Instance() { static RealmList instance; return &instance; } // Load the realm list from the database void RealmList::Initialize(Acore::Asio::IoContext& ioContext, uint32 updateInterval) { _updateInterval = updateInterval; _updateTimer = std::make_unique(ioContext); _resolver = std::make_unique(ioContext); LoadBuildInfo(); // Get the content of the realmlist table in the database UpdateRealms(boost::system::error_code()); } void RealmList::Close() { _updateTimer->cancel(); } void RealmList::LoadBuildInfo() { // 0 1 2 3 4 5 6 if (auto result = LoginDatabase.Query("SELECT majorVersion, minorVersion, bugfixVersion, hotfixVersion, build, winChecksumSeed, macChecksumSeed FROM build_info ORDER BY build ASC")) { for (auto const& fields : *result) { RealmBuildInfo& build = _builds.emplace_back(); build.MajorVersion = fields[0].Get(); build.MinorVersion = fields[1].Get(); build.BugfixVersion = fields[2].Get(); std::string hotfixVersion = fields[3].Get(); if (hotfixVersion.length() < build.HotfixVersion.size()) { std::copy(hotfixVersion.begin(), hotfixVersion.end(), build.HotfixVersion.begin()); } else { std::fill(hotfixVersion.begin(), hotfixVersion.end(), '\0'); } build.Build = fields[4].Get(); std::string windowsHash = fields[5].Get(); if (windowsHash.length() == build.WindowsHash.size() * 2) { HexStrToByteArray(windowsHash, build.WindowsHash); } std::string macHash = fields[6].Get(); if (macHash.length() == build.MacHash.size() * 2) { HexStrToByteArray(macHash, build.MacHash); } } } } void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name, boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 realmTimezone, AccountTypes allowedSecurityLevel, float population) { // Create new if not exist or update existed Realm& realm = _realms[id]; realm.Id = id; realm.Build = build; realm.Name = name; realm.Type = icon; realm.Flags = flag; realm.Timezone = realmTimezone; realm.AllowedSecurityLevel = allowedSecurityLevel; realm.PopulationLevel = population; if (!realm.ExternalAddress || *realm.ExternalAddress != address) { realm.ExternalAddress = std::make_unique(std::move(address)); } if (!realm.LocalAddress || *realm.LocalAddress != localAddr) { realm.LocalAddress = std::make_unique(std::move(localAddr)); } if (!realm.LocalSubnetMask || *realm.LocalSubnetMask != localSubmask) { realm.LocalSubnetMask = std::make_unique(std::move(localSubmask)); } realm.Port = port; } void RealmList::UpdateRealms(boost::system::error_code const& error) { if (error) { // Skip update if have errors return; } LOG_DEBUG("server.authserver", "Updating Realm List..."); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST); PreparedQueryResult result = LoginDatabase.Query(stmt); std::map existingRealms; for (auto const& [handle, realm] : _realms) { existingRealms[handle] = realm.Name; } _realms.clear(); // Circle through results and add them to the realm map if (result) { for (auto const& fields : *result) { try { uint32 realmId = fields[0].Get(); std::string name = fields[1].Get(); std::string externalAddressString = fields[2].Get(); std::string localAddressString = fields[3].Get(); std::string localSubmaskString = fields[4].Get(); uint16 port = fields[5].Get(); Optional externalAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), externalAddressString, ""); if (!externalAddress) { LOG_ERROR("server.authserver", "Could not resolve address {} for realm \"{}\" id {}", externalAddressString, name, realmId); continue; } Optional localAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), localAddressString, ""); if (!localAddress) { LOG_ERROR("server.authserver", "Could not resolve localAddress {} for realm \"{}\" id {}", localAddressString, name, realmId); continue; } Optional localSubmask = _resolver->Resolve(boost::asio::ip::tcp::v4(), localSubmaskString, ""); if (!localSubmask) { LOG_ERROR("server.authserver", "Could not resolve localSubnetMask {} for realm \"{}\" id {}", localSubmaskString, name, realmId); continue; } uint8 icon = fields[6].Get(); if (icon == REALM_TYPE_FFA_PVP) { icon = REALM_TYPE_PVP; } if (icon >= MAX_CLIENT_REALM_TYPE) { icon = REALM_TYPE_NORMAL; } auto flag = RealmFlags(fields[7].Get()); uint8 realmTimezone = fields[8].Get(); uint8 allowedSecurityLevel = fields[9].Get(); float pop = fields[10].Get(); uint32 build = fields[11].Get(); RealmHandle id{ realmId }; UpdateRealm(id, build, name, externalAddress->address(), localAddress->address(), localSubmask->address(), port, icon, flag, realmTimezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop); if (!existingRealms.count(id)) { LOG_INFO("server.authserver", "Added realm \"{}\" at {}:{}.", name, externalAddressString, port); } else { LOG_DEBUG("server.authserver", "Updating realm \"{}\" at {}:{}.", name, externalAddressString, port); } existingRealms.erase(id); } catch (std::exception const& ex) { LOG_ERROR("server.authserver", "Realmlist::UpdateRealms has thrown an exception: {}", ex.what()); ABORT(); } } } for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr) LOG_INFO("server.authserver", "Removed realm \"{}\".", itr->second); if (_updateInterval) { _updateTimer->expires_at(Acore::Asio::SteadyTimer::GetExpirationTime(_updateInterval)); _updateTimer->async_wait([this](boost::system::error_code const& errorCode){ UpdateRealms(errorCode); }); } } Realm const* RealmList::GetRealm(RealmHandle const& id) const { auto itr = _realms.find(id); if (itr != _realms.end()) { return &itr->second; } return nullptr; } RealmBuildInfo const* RealmList::GetBuildInfo(uint32 build) const { for (RealmBuildInfo const& clientBuild : _builds) { if (clientBuild.Build == build) { return &clientBuild; } } return nullptr; }