mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 01:11:29 +00:00
This removes the separate Expedition class and moves lockout code and
/dz command handlers into DynamicZone classes. It also refactors some
code to reduce bloat and some database usage.
This completes the effort of moving everything to DynamicZone that
started when implementing shared tasks. It also makes sense to do this
since expeditions are just dynamic zones internally despite dzs being
used for other types. Expedition specific things are just handled with
dz type checks.
Functionally nothing should change. This is mainly internal refactoring
and moving code around along with some bug fixes and reduced database
usage.
Main changes:
- The `expeditions` database table has been removed
- Expeditions no longer use a separate id, the expedition id is just the dz id
- Expedition lock state and replay timer option were moved to the
`dynamic_zones` table
- Expeditions no longer have a separate cache from dynamic zones
- Expedition creation no longer has every zone query the database to cache it
- Expedition internal lockouts are now stored on DynamicZone
- The `expedition_lockouts` table has been renamed to `dynamic_zone_lockouts`
- Fixed a small bug with the UpdateLockoutDuration api where the
internal lockout would get the time added twice in memory in the
initiating zone (this api is likely rarely used)
- Fixed an issue where use of the group/raid DoesAnyMemberHaveExpeditionLockout
api would query once for every out of zone character.
- This api now checks all members in the current zone first and only
performs a single bulk query for out of zone members if that check
is exhausted
- Deprecated the max_check_count param of DoesAnyMemberHaveExpeditionLockout,
the quest api still exists to avoid api break but a passed arg has no effect
631 lines
19 KiB
C++
631 lines
19 KiB
C++
#include "../common/content/world_content_service.h"
|
|
#include "../common/emu_constants.h"
|
|
#include "../common/eqemu_logsys.h"
|
|
#include "../common/http/httplib.h"
|
|
#include "../common/http/uri.h"
|
|
#include "../common/net/console_server.h"
|
|
#include "../common/net/servertalk_server.h"
|
|
#include "../common/repositories/character_expedition_lockouts_repository.h"
|
|
#include "../common/repositories/character_task_timers_repository.h"
|
|
#include "../common/rulesys.h"
|
|
#include "../common/strings.h"
|
|
#include "adventure_manager.h"
|
|
#include "dynamic_zone_manager.h"
|
|
#include "login_server_list.h"
|
|
#include "shared_task_manager.h"
|
|
#include "ucs.h"
|
|
#include "wguild_mgr.h"
|
|
#include "world_boot.h"
|
|
#include "world_config.h"
|
|
#include "world_event_scheduler.h"
|
|
#include "world_server_cli.h"
|
|
#include "../common/zone_store.h"
|
|
#include "worlddb.h"
|
|
#include "zonelist.h"
|
|
#include "zoneserver.h"
|
|
#include "../common/ip_util.h"
|
|
#include "../common/zone_store.h"
|
|
#include "../common/path_manager.h"
|
|
#include "../common/database/database_update.h"
|
|
|
|
extern ZSList zoneserver_list;
|
|
extern WorldConfig Config;
|
|
|
|
auto mutex = new Mutex;
|
|
|
|
void WorldBoot::GMSayHookCallBackProcessWorld(uint16 log_category, const char *func, std::string message)
|
|
{
|
|
// we don't want to loop up with chat messages
|
|
if (message.find("OP_SpecialMesg") != std::string::npos) {
|
|
return;
|
|
}
|
|
|
|
// Cut messages down to 4000 max to prevent client crash
|
|
if (!message.empty()) {
|
|
message = message.substr(0, 4000);
|
|
}
|
|
|
|
// Replace Occurrences of % or MessageStatus will crash
|
|
Strings::FindReplace(message, std::string("%"), std::string("."));
|
|
|
|
if (message.find('\n') != std::string::npos) {
|
|
auto message_split = Strings::Split(message, '\n');
|
|
|
|
for (size_t iter = 0; iter < message_split.size(); ++iter) {
|
|
zoneserver_list.SendEmoteMessage(
|
|
0,
|
|
0,
|
|
AccountStatus::QuestTroupe,
|
|
LogSys.GetGMSayColorFromCategory(log_category),
|
|
fmt::format(
|
|
" {}{}",
|
|
(iter == 0 ? " ---" : ""),
|
|
message_split[iter]
|
|
).c_str()
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
zoneserver_list.SendEmoteMessage(
|
|
0,
|
|
0,
|
|
AccountStatus::QuestTroupe,
|
|
LogSys.GetGMSayColorFromCategory(log_category),
|
|
"%s",
|
|
fmt::format("[{}] [{}] {}", Logs::LogCategoryName[log_category], func, message).c_str()
|
|
);
|
|
}
|
|
|
|
bool WorldBoot::HandleCommandInput(int argc, char **argv)
|
|
{
|
|
// command handler
|
|
if (argc > 1) {
|
|
LogSys.SilenceConsoleLogging();
|
|
path.LoadPaths();
|
|
WorldConfig::LoadConfig();
|
|
LoadDatabaseConnections();
|
|
RuleManager::Instance()->LoadRules(&database, "default", false);
|
|
LogSys.EnableConsoleLogging();
|
|
WorldserverCLI::CommandHandler(argc, argv);
|
|
}
|
|
|
|
// database version
|
|
uint32 database_version = CURRENT_BINARY_DATABASE_VERSION;
|
|
uint32 bots_database_version = RuleB(Bots, Enabled) ? CURRENT_BINARY_BOTS_DATABASE_VERSION : 0;
|
|
if (argc >= 2) {
|
|
if (strcasecmp(argv[1], "db_version") == 0) {
|
|
std::cout << "Binary Database Version: " << database_version << " : " << bots_database_version << std::endl;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool WorldBoot::LoadServerConfig()
|
|
{
|
|
LogInfo("Loading server configuration");
|
|
if (!WorldConfig::LoadConfig()) {
|
|
LogError("Loading server configuration failed");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool WorldBoot::LoadDatabaseConnections()
|
|
{
|
|
const auto c = EQEmuConfig::get();
|
|
|
|
LogInfo(
|
|
"Connecting to MySQL [{}]@[{}]:[{}]",
|
|
c->DatabaseUsername.c_str(),
|
|
c->DatabaseHost.c_str(),
|
|
c->DatabasePort
|
|
);
|
|
|
|
if (!database.Connect(
|
|
c->DatabaseHost.c_str(),
|
|
c->DatabaseUsername.c_str(),
|
|
c->DatabasePassword.c_str(),
|
|
c->DatabaseDB.c_str(),
|
|
c->DatabasePort
|
|
)) {
|
|
LogError("Cannot continue without a database connection");
|
|
|
|
return false;
|
|
}
|
|
|
|
// Multi-tenancy - content database
|
|
if (!c->ContentDbHost.empty()) {
|
|
if (!content_db.Connect(
|
|
c->ContentDbHost.c_str(),
|
|
c->ContentDbUsername.c_str(),
|
|
c->ContentDbPassword.c_str(),
|
|
c->ContentDbName.c_str(),
|
|
c->ContentDbPort,
|
|
"content"
|
|
)) {
|
|
LogError("Cannot continue without a content database connection");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
content_db.SetMySQL(database);
|
|
// when database and content_db share the same underlying mysql connection
|
|
// it needs to be protected by a shared mutex otherwise we produce concurrency issues
|
|
// when database actions are occurring in different threads
|
|
database.SetMutex(mutex);
|
|
content_db.SetMutex(mutex);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int get_file_size(const std::string &filename) // path to file
|
|
{
|
|
FILE *p_file = NULL;
|
|
p_file = fopen(filename.c_str(), "rb");
|
|
fseek(p_file, 0, SEEK_END);
|
|
int size = ftell(p_file);
|
|
fclose(p_file);
|
|
return size;
|
|
}
|
|
|
|
extern LoginServerList loginserverlist;
|
|
|
|
void WorldBoot::RegisterLoginservers()
|
|
{
|
|
const auto c = EQEmuConfig::get();
|
|
|
|
if (c->LoginCount == 0) {
|
|
if (c->LoginHost.length()) {
|
|
loginserverlist.Add(
|
|
c->LoginHost.c_str(),
|
|
c->LoginPort,
|
|
c->LoginAccount.c_str(),
|
|
c->LoginPassword.c_str(),
|
|
c->LoginLegacy
|
|
);
|
|
LogInfo("Added loginserver [{}]:[{}]", c->LoginHost.c_str(), c->LoginPort);
|
|
}
|
|
}
|
|
else {
|
|
LinkedList<LoginConfig *> loginlist = c->loginlist;
|
|
LinkedListIterator<LoginConfig *> iterator(loginlist);
|
|
iterator.Reset();
|
|
while (iterator.MoreElements()) {
|
|
if (iterator.GetData()->LoginHost.length()) {
|
|
loginserverlist.Add(
|
|
iterator.GetData()->LoginHost.c_str(),
|
|
iterator.GetData()->LoginPort,
|
|
iterator.GetData()->LoginAccount.c_str(),
|
|
iterator.GetData()->LoginPassword.c_str(),
|
|
iterator.GetData()->LoginLegacy
|
|
);
|
|
|
|
LogInfo(
|
|
"Added loginserver [{}]:[{}]",
|
|
iterator.GetData()->LoginHost.c_str(),
|
|
iterator.GetData()->LoginPort
|
|
);
|
|
}
|
|
iterator.Advance();
|
|
}
|
|
}
|
|
}
|
|
|
|
extern SharedTaskManager shared_task_manager;
|
|
extern AdventureManager adventure_manager;
|
|
extern WorldEventScheduler event_scheduler;
|
|
|
|
bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
|
{
|
|
// logging system init
|
|
auto logging = LogSys.SetDatabase(&database)
|
|
->SetLogPath(path.GetLogPath())
|
|
->LoadLogDatabaseSettings();
|
|
|
|
LogSys.SetDiscordHandler(&WorldBoot::DiscordWebhookMessageHandler);
|
|
|
|
const auto c = EQEmuConfig::get();
|
|
if (c->auto_database_updates) {
|
|
LogInfo("Checking Database Conversions");
|
|
|
|
auto *r = RuleManager::Instance();
|
|
r->LoadRules(&database, "default", false);
|
|
if (!RuleB(Bots, Enabled) && database.DoesTableExist("bot_data")) {
|
|
LogInfo("Bot tables found but rule not enabled, enabling");
|
|
r->SetRule("Bots:Enabled", "true", &database, true, true);
|
|
}
|
|
|
|
DatabaseUpdate update{};
|
|
update.SetDatabase(&database)
|
|
->SetContentDatabase(&content_db)
|
|
->CheckDbUpdates();
|
|
}
|
|
|
|
if (RuleB(Logging, WorldGMSayLogging)) {
|
|
logging->SetGMSayHandler(&WorldBoot::GMSayHookCallBackProcessWorld);
|
|
}
|
|
|
|
logging->StartFileLogs();
|
|
|
|
LogInfo("Loading variables");
|
|
database.LoadVariables();
|
|
|
|
std::string hotfix_name;
|
|
if (database.GetVariable("hotfix_name", hotfix_name)) {
|
|
if (!hotfix_name.empty()) {
|
|
LogInfo("Current hotfix in use: [{}]", hotfix_name.c_str());
|
|
}
|
|
}
|
|
|
|
guild_mgr.SetDatabase(&database)->SetContentDatabase(&content_db);
|
|
|
|
LogInfo("Purging expired data buckets");
|
|
database.PurgeAllDeletedDataBuckets();
|
|
|
|
LogInfo("Loading zones");
|
|
|
|
zone_store.LoadZones(content_db);
|
|
|
|
if (zone_store.GetZones().empty()) {
|
|
LogError("Failed to load zones data, check your schema for possible errors");
|
|
return 1;
|
|
}
|
|
|
|
LogInfo("Clearing groups");
|
|
database.ClearGroup();
|
|
LogInfo("Clearing raids");
|
|
database.ClearRaid();
|
|
database.ClearRaidDetails();
|
|
database.ClearRaidLeader();
|
|
LogInfo("Clearing guild online status");
|
|
database.ClearGuildOnlineStatus();
|
|
LogInfo("Clearing inventory snapshots");
|
|
database.ClearInvSnapshots();
|
|
LogInfo("Loading items");
|
|
LogInfo("Clearing trader table details");
|
|
database.ClearTraderDetails();
|
|
database.ClearBuyerDetails();
|
|
LogInfo("Clearing buyer table details");
|
|
|
|
if (RuleB(Bots, Enabled)) {
|
|
LogInfo("Clearing [bot_pet_buffs] table of stale entries");
|
|
database.QueryDatabase(
|
|
"DELETE FROM bot_pet_buffs WHERE NOT EXISTS (SELECT * FROM bot_pets WHERE bot_pets.pets_index = bot_pet_buffs.pets_index)"
|
|
);
|
|
}
|
|
|
|
if (!content_db.LoadItems(hotfix_name)) {
|
|
LogError("Error: Could not load item data. But ignoring");
|
|
}
|
|
|
|
guild_mgr.LoadGuilds();
|
|
guild_mgr.LoadTributes();
|
|
|
|
//rules:
|
|
{
|
|
if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) {
|
|
LogInfo("Failed to process 'Orphaned Rules' update operation.");
|
|
}
|
|
|
|
if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) {
|
|
LogInfo("Failed to process 'Injected Rules' for ruleset 'default' update operation.");
|
|
}
|
|
|
|
std::string tmp;
|
|
if (database.GetVariable("RuleSet", tmp)) {
|
|
LogInfo("Loading rule set [{}]", tmp.c_str());
|
|
|
|
if (!RuleManager::Instance()->LoadRules(&database, tmp, false)) {
|
|
LogInfo("Failed to load ruleset [{}], falling back to defaults", tmp.c_str());
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (!RuleManager::Instance()->LoadRules(&database, "default", false)) {
|
|
LogInfo("No rule set configured, using default rules");
|
|
}
|
|
}
|
|
|
|
if (!RuleManager::Instance()->RestoreRuleNotes(&database)) {
|
|
LogInfo("Failed to process 'Restore Rule Notes' update operation.");
|
|
}
|
|
}
|
|
|
|
EQ::InitializeDynamicLookups();
|
|
|
|
if (RuleB(World, ClearTempMerchantlist)) {
|
|
LogInfo("Clearing temporary merchant lists");
|
|
database.ClearMerchantTemp();
|
|
}
|
|
|
|
LogInfo("Loading EQ time of day");
|
|
TimeOfDay_Struct eqTime{};
|
|
time_t realtime;
|
|
eqTime = database.LoadTime(realtime);
|
|
zoneserver_list.worldclock.SetCurrentEQTimeOfDay(eqTime, realtime);
|
|
|
|
LogInfo("Deleted [{}] stale player corpses from database", database.DeleteStalePlayerCorpses());
|
|
|
|
LogInfo("Loading adventures");
|
|
if (!adventure_manager.LoadAdventureTemplates()) {
|
|
LogInfo("Unable to load adventure templates");
|
|
}
|
|
|
|
if (!adventure_manager.LoadAdventureEntries()) {
|
|
LogInfo("Unable to load adventure templates");
|
|
}
|
|
|
|
adventure_manager.LoadLeaderboardInfo();
|
|
|
|
LogInfo("Purging expired dynamic zones and members");
|
|
dynamic_zone_manager.PurgeExpiredDynamicZones();
|
|
|
|
LogInfo("Purging expired character expedition lockouts");
|
|
CharacterExpeditionLockoutsRepository::DeleteWhere(database, "expire_time <= NOW()");
|
|
|
|
LogInfo("Purging expired character task timers");
|
|
CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()");
|
|
|
|
LogInfo("Purging expired instances");
|
|
database.PurgeExpiredInstances();
|
|
|
|
LogInfo("Loading dynamic zones");
|
|
dynamic_zone_manager.LoadTemplates();
|
|
dynamic_zone_manager.CacheAllFromDatabase();
|
|
|
|
LogInfo("Loading char create info");
|
|
content_db.LoadCharacterCreateAllocations();
|
|
content_db.LoadCharacterCreateCombos();
|
|
|
|
LogInfo("Initializing [EventScheduler]");
|
|
event_scheduler.SetDatabase(&database)->LoadScheduledEvents();
|
|
|
|
LogInfo("Initializing [WorldContentService]");
|
|
content_service.SetDatabase(&database)
|
|
->SetContentDatabase(&content_db)
|
|
->SetExpansionContext()
|
|
->ReloadContentFlags();
|
|
|
|
LogInfo("Initializing [SharedTaskManager]");
|
|
shared_task_manager.SetDatabase(&database)
|
|
->SetContentDatabase(&content_db)
|
|
->LoadTaskData()
|
|
->LoadSharedTaskState();
|
|
|
|
LogInfo("Purging expired shared tasks");
|
|
shared_task_manager.PurgeExpiredSharedTasks();
|
|
|
|
LogInfo("Cleaning up instance corpses");
|
|
database.CleanupInstanceCorpses();
|
|
|
|
return true;
|
|
}
|
|
|
|
void WorldBoot::CheckForPossibleConfigurationIssues()
|
|
{
|
|
const auto c = EQEmuConfig::get();
|
|
|
|
if (c->DisableConfigChecks) {
|
|
LogInfo("Configuration checking [disabled]");
|
|
return;
|
|
}
|
|
|
|
const std::string local_address = IpUtil::GetLocalIPAddress();
|
|
const std::string public_address = IpUtil::GetPublicIPAddress();
|
|
const std::string config_file = "eqemu_config.json";
|
|
const std::ifstream is_in_docker("/.dockerenv");
|
|
|
|
if (local_address.empty() && public_address.empty()) {
|
|
LogInfo("Configuration check, probes failed for local and public address, returning");
|
|
return;
|
|
}
|
|
|
|
LogInfo("Checking for possible configuration issues");
|
|
LogInfo("To disable configuration checks, set [server.disable_config_checks] to [true] in [{}]", config_file);
|
|
|
|
std::string config_address = c->WorldAddress;
|
|
if (!IpUtil::IsIPAddress(config_address)) {
|
|
config_address = IpUtil::DNSLookupSync(c->WorldAddress, 9000);
|
|
LogInfo(
|
|
"World config address using DNS [{}] resolves to [{}]",
|
|
c->WorldAddress,
|
|
config_address
|
|
);
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
// lan detection
|
|
if (local_address != public_address
|
|
&& IpUtil::IsIpInPrivateRfc1918(local_address)
|
|
&& local_address != c->LocalAddress
|
|
&& !is_in_docker
|
|
) {
|
|
LogWarning("# LAN detection (Configuration)");
|
|
LogWarning("");
|
|
LogWarning("You appear to be on a LAN and your localaddress may not be properly set!");
|
|
LogWarning("This can prevent local clients from properly connecting to your server");
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#world]");
|
|
LogWarning("");
|
|
LogWarning("Config file [{}] path [server.world] variable [localaddress]", config_file);
|
|
LogWarning("");
|
|
LogWarning("Local address (eqemu_config) value [{}] detected value [{}]", c->LocalAddress, local_address);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// docker configuration
|
|
if (
|
|
(
|
|
(c->LocalAddress.empty() && config_address.empty()) ||
|
|
(config_address != public_address)
|
|
)
|
|
&& is_in_docker
|
|
) {
|
|
LogWarning("# Docker Configuration (Configuration)");
|
|
LogWarning("");
|
|
LogWarning("You appear to running EQEmu in a docker container");
|
|
LogWarning("In order for networking to work properly you will need to properly configure your server");
|
|
LogWarning("");
|
|
LogWarning(
|
|
"If your Docker host is on a [LAN] or behind a NAT / Firewall, your [localaddress] variable under [server.world] will need to");
|
|
LogWarning(
|
|
"be set to your LAN address on the host, not the container address. [address] will need to be your public address");
|
|
LogWarning("");
|
|
LogWarning(
|
|
"If your Docker host is directly on the [public internet], your [localaddress] variable under [server.world] can be set to [127.0.0.1]."
|
|
);
|
|
LogWarning("");
|
|
LogWarning("[address] will need to be your public address");
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#world]");
|
|
LogWarning("");
|
|
LogWarning("Config file [{}] path [server.world] variable(s) [localaddress] [address]", config_file);
|
|
LogWarning("");
|
|
LogWarning("Local address (eqemu_config) value [{}] detected value [{}]", c->LocalAddress, local_address);
|
|
LogWarning(
|
|
"Public address (eqemu_config) value [{}] detected value [{}]",
|
|
config_address,
|
|
public_address
|
|
);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// docker LAN not set
|
|
if (c->LocalAddress.empty() && is_in_docker) {
|
|
LogWarning("# Docker LAN (Configuration)");
|
|
LogWarning("");
|
|
LogWarning("You appear to running EQEmu in a docker container");
|
|
LogWarning(
|
|
"Your local address does not appear to be set, this may not be an issue if your deployment is not on a LAN"
|
|
);
|
|
LogWarning("");
|
|
LogWarning(
|
|
"If your Docker host is on a [LAN] or behind a NAT / Firewall, your [localaddress] variable under [server.world] will need to");
|
|
LogWarning(
|
|
"be set to your LAN address on the host, not the container address. [address] will need to be your public address");
|
|
LogWarning("");
|
|
LogWarning(
|
|
"If your Docker host is directly on the [public internet], your [localaddress] variable under [server.world] can be set to [127.0.0.1]."
|
|
);
|
|
LogWarning("");
|
|
LogWarning("[address] will need to be your public address");
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#world]");
|
|
LogWarning("");
|
|
LogWarning("Config file [{}] path [server.world] variable(s) [localaddress] [address]", config_file);
|
|
LogWarning("");
|
|
LogWarning("Local address (eqemu_config) value [{}] detected value [{}]", c->LocalAddress, local_address);
|
|
LogWarning(
|
|
"Public address (eqemu_config) value [{}] detected value [{}]",
|
|
config_address,
|
|
public_address
|
|
);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// public address different from configuration
|
|
if (!config_address.empty() && public_address != config_address) {
|
|
LogWarning("# Public address (Configuration)");
|
|
LogWarning("");
|
|
LogWarning("Your configured public address appears to be different from what's detected!");
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#world]");
|
|
LogWarning("");
|
|
LogWarning("Config file [{}] path [server.world] variable [address]", config_file);
|
|
LogWarning("");
|
|
LogWarning(
|
|
"Public address (eqemu_config) value [{}] detected value [{}]",
|
|
config_address,
|
|
public_address
|
|
);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// public address set to meta-address
|
|
if (config_address == "0.0.0.0") {
|
|
LogWarning("# Public meta-address (Configuration)");
|
|
LogWarning("");
|
|
LogWarning("Your configured public address is set to a meta-address (0.0.0.0) (all-interfaces)");
|
|
LogWarning(
|
|
"The meta-address may not work properly and it is recommended you configure your public address explicitly");
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#world]");
|
|
LogWarning("");
|
|
LogWarning("Config file [{}] path [server.world] variable [address]", config_file);
|
|
LogWarning("");
|
|
LogWarning(
|
|
"Public address (eqemu_config) value [{}] detected value [{}]",
|
|
config_address,
|
|
public_address
|
|
);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// local address set to meta-address
|
|
if (c->LocalAddress == "0.0.0.0") {
|
|
LogWarning("# Local meta-address (Configuration)");
|
|
LogWarning("");
|
|
LogWarning("Your configured local address is set to a meta-address (0.0.0.0) (all-interfaces)");
|
|
LogWarning(
|
|
"The meta-address may not work properly and it is recommended you configure your local address explicitly"
|
|
);
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#world]");
|
|
LogWarning("");
|
|
LogWarning("Config file [{}] path [server.world] variable [localaddress]", config_file);
|
|
LogWarning("");
|
|
LogWarning("Local address (eqemu_config) value [{}] detected value [{}]", c->LocalAddress, local_address);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// ucs (public)
|
|
if (
|
|
(!config_address.empty() && c->GetUCSHost() != config_address)
|
|
) {
|
|
LogWarning("# UCS Address Mailhost (Configuration)");
|
|
LogWarning("");
|
|
LogWarning(
|
|
"UCS (Universal Chat Service) mail or chat appears to use a different address from your main world address"
|
|
);
|
|
LogWarning("This can result in a chat service that doesn't network properly");
|
|
LogWarning("");
|
|
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#mailserver]");
|
|
LogWarning("");
|
|
LogWarning(
|
|
"[server.world.address] value [{}] [server.ucs.host] [{}]",
|
|
config_address,
|
|
c->GetUCSHost()
|
|
);
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
void WorldBoot::Shutdown()
|
|
{
|
|
safe_delete(mutex);
|
|
}
|
|
|
|
void WorldBoot::SendDiscordMessage(int webhook_id, const std::string &message)
|
|
{
|
|
if (UCSLink.IsConnected()) {
|
|
auto pack = new ServerPacket(ServerOP_DiscordWebhookMessage, sizeof(DiscordWebhookMessage_Struct) + 1);
|
|
auto *q = (DiscordWebhookMessage_Struct *) pack->pBuffer;
|
|
|
|
strn0cpy(q->message, message.c_str(), 2000);
|
|
q->webhook_id = webhook_id;
|
|
|
|
UCSLink.SendPacket(pack);
|
|
|
|
safe_delete(pack);
|
|
}
|
|
}
|
|
|