zaela-Server/zone/main.cpp

626 lines
16 KiB
C++
Raw Permalink Normal View History

2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
2017-04-02 20:03:51 -07:00
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.org)
2013-02-16 16:14:39 -08:00
2017-04-02 20:03:51 -07:00
This program 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; version 2 of the License.
2013-05-06 13:07:41 -04:00
2017-04-02 20:03:51 -07:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
2013-05-06 13:07:41 -04:00
2017-04-02 20:03:51 -07:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2013-02-16 16:14:39 -08:00
*/
#define DONT_SHARED_OPCODES
#define PLATFORM_ZONE 1
2013-02-16 16:14:39 -08:00
#include "../common/global_define.h"
#include "../common/features.h"
#include "../common/queue.h"
#include "../common/timer.h"
#include "../common/eq_packet_structs.h"
#include "../common/mutex.h"
#include "../common/version.h"
#include "../common/packet_dump_file.h"
#include "../common/opcodemgr.h"
#include "../common/guilds.h"
#include "../common/eq_stream_ident.h"
#include "../common/patches/patches.h"
#include "../common/rulesys.h"
2019-02-04 07:02:27 -05:00
#include "../common/profanity_manager.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/platform.h"
#include "../common/crash.h"
#include "../common/ipc_mutex.h"
#include "../common/memory_mapped_file.h"
#include "../common/eqemu_exception.h"
2013-05-09 21:25:50 -07:00
#include "../common/spdat.h"
#include "../common/eqemu_logsys.h"
2019-05-16 00:12:21 -07:00
#include "api_service.h"
#include "zone_config.h"
#include "masterentity.h"
#include "worldserver.h"
#include "zone.h"
#include "queryserv.h"
#include "command.h"
#ifdef BOTS
#include "bot_command.h"
#endif
#include "zone_config.h"
#include "titles.h"
#include "guild_mgr.h"
#include "tasks.h"
#include "quest_parser_collection.h"
#include "embparser.h"
#include "lua_parser.h"
2013-05-09 21:25:50 -07:00
#include "questmgr.h"
#include "npc_scale_manager.h"
2013-02-16 16:14:39 -08:00
#include "../common/event/event_loop.h"
#include "../common/event/timer.h"
#include "../common/net/eqstream.h"
2019-03-09 23:08:38 -06:00
#include "../common/net/servertalk_server.h"
#include <iostream>
#include <string>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <ctime>
#include <thread>
#include <chrono>
#ifdef _CRTDBG_MAP_ALLOC
2017-04-02 20:03:51 -07:00
#undef new
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#ifdef _WINDOWS
2017-04-02 20:03:51 -07:00
#include <conio.h>
#include <process.h>
#else
2017-04-02 20:03:51 -07:00
#include <pthread.h>
#include "../common/unix.h"
#endif
#ifdef __FreeBSD__
#include <pthread_np.h>
#endif
extern volatile bool is_zone_loaded;
EntityList entity_list;
WorldServer worldserver;
uint32 numclients = 0;
char errorname[32];
extern Zone* zone;
2013-02-16 16:14:39 -08:00
npcDecayTimes_Struct npcCorpseDecayTimes[100];
TitleManager title_manager;
QueryServ *QServ = 0;
TaskManager *taskmanager = 0;
NpcScaleManager *npc_scale_manager;
QuestParserCollection *parse = 0;
EQEmuLogSys LogSys;
2013-05-06 13:07:41 -04:00
const SPDat_Spell_Struct* spells;
int32 SPDAT_RECORDS = -1;
2016-05-20 21:03:34 -05:00
const ZoneConfig *Config;
double frame_time = 0.0;
2013-02-16 16:14:39 -08:00
void Shutdown();
2019-10-13 18:49:16 -07:00
void UpdateWindowTitle(char* iNewTitle);
void CatchSignal(int sig_num);
2013-02-16 16:14:39 -08:00
extern void MapOpcodes();
int main(int argc, char** argv) {
2017-04-02 20:03:51 -07:00
RegisterExecutablePlatform(ExePlatformZone);
LogSys.LoadLogSettingsDefaults();
2017-04-02 20:03:51 -07:00
set_exception_handler();
#ifdef USE_MAP_MMFS
if (argc == 3 && strcasecmp(argv[1], "convert_map") == 0) {
if (!ZoneConfig::LoadConfig())
return 1;
Config = ZoneConfig::get();
std::string mapfile = argv[2];
std::transform(mapfile.begin(), mapfile.end(), mapfile.begin(), ::tolower);
std::string filename = Config->MapDir;
filename += mapfile;
auto m = new Map();
auto success = m->Load(filename, true);
delete m;
std::cout << mapfile.c_str() << " conversion " << (success ? "succeeded" : "failed") << std::endl;
2017-04-02 20:03:51 -07:00
return 0;
}
#endif /*USE_MAP_MMFS*/
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload intensive or metric based logging to a remote server process that could exist on another server entirely Implemented Player Event Logging Types (Go to table `qs_player_events`): 1 = Player_Log_Quest, 2 = Player_Log_Zoning, 3 = Player_Log_Deaths, 4 = Player_Log_Connect_State, 5 = Player_Log_Levels, 6 = Player_Log_Keyring_Addition, 7 = Player_Log_QGlobal_Update, 8 = Player_Log_Task_Updates, 9 = Player_Log_AA_Purchases, 10 = Player_Log_Trade_Skill_Events, 11 = Player_Log_Issued_Commands, 12 = Player_Log_Money_Transactions, 13 = Player_Log_Alternate_Currency_Transactions, - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon Changed all QS Error related logging to 'QUERYSERV__ERROR' (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( Implemented Perl Quest objects (LUA still needed to be exported): - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` Added MySQL Tables: - `qs_player_aa_rate_hourly` - `qs_player_events` - Source table structures from: - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly To get the complete QueryServ schema, source from here: - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql Added rules for each logging type, source rules here with them enabled by default: - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql Spawn related logging cleanup General code cleanup Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
QServ = new QueryServ;
2019-09-01 21:04:58 -05:00
LogInfo("Loading server configuration..");
2017-04-02 20:03:51 -07:00
if (!ZoneConfig::LoadConfig()) {
2019-09-01 22:05:44 -05:00
LogError("Loading server configuration failed");
return 1;
}
2016-05-20 21:03:34 -05:00
Config = ZoneConfig::get();
const char *zone_name;
uint32 instance_id = 0;
std::string z_name;
2017-04-02 20:03:51 -07:00
if (argc == 4) {
instance_id = atoi(argv[3]);
2013-02-16 16:14:39 -08:00
worldserver.SetLauncherName(argv[2]);
auto zone_port = SplitString(argv[1], ':');
2017-04-02 20:03:51 -07:00
if (!zone_port.empty()) {
z_name = zone_port[0];
}
2017-04-02 20:03:51 -07:00
if (zone_port.size() > 1) {
std::string p_name = zone_port[1];
Config->SetZonePort(atoi(p_name.c_str()));
}
worldserver.SetLaunchedName(z_name.c_str());
2017-04-02 20:03:51 -07:00
if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
zone_name = ".";
}
else {
zone_name = z_name.c_str();
}
2017-04-02 20:03:51 -07:00
}
else if (argc == 3) {
2013-02-16 16:14:39 -08:00
worldserver.SetLauncherName(argv[2]);
auto zone_port = SplitString(argv[1], ':');
2017-04-02 20:03:51 -07:00
if (!zone_port.empty()) {
z_name = zone_port[0];
}
2017-04-02 20:03:51 -07:00
if (zone_port.size() > 1) {
std::string p_name = zone_port[1];
Config->SetZonePort(atoi(p_name.c_str()));
}
worldserver.SetLaunchedName(z_name.c_str());
2017-04-02 20:03:51 -07:00
if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
2013-02-16 16:14:39 -08:00
zone_name = ".";
2017-04-02 20:03:51 -07:00
}
else {
zone_name = z_name.c_str();
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
}
else if (argc == 2) {
2013-02-16 16:14:39 -08:00
worldserver.SetLauncherName("NONE");
auto zone_port = SplitString(argv[1], ':');
2017-04-02 20:03:51 -07:00
if (!zone_port.empty()) {
z_name = zone_port[0];
}
2017-04-02 20:03:51 -07:00
if (zone_port.size() > 1) {
std::string p_name = zone_port[1];
Config->SetZonePort(atoi(p_name.c_str()));
}
worldserver.SetLaunchedName(z_name.c_str());
2017-04-02 20:03:51 -07:00
if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
2013-02-16 16:14:39 -08:00
zone_name = ".";
}
else {
zone_name = z_name.c_str();
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
}
else {
2013-02-16 16:14:39 -08:00
zone_name = ".";
worldserver.SetLaunchedName(".");
worldserver.SetLauncherName("NONE");
}
2019-09-01 21:04:58 -05:00
LogInfo("Connecting to MySQL... ");
2013-02-16 16:14:39 -08:00
if (!database.Connect(
Config->DatabaseHost.c_str(),
Config->DatabaseUsername.c_str(),
Config->DatabasePassword.c_str(),
Config->DatabaseDB.c_str(),
Config->DatabasePort)) {
2019-09-01 22:05:44 -05:00
LogError("Cannot continue without a database connection");
return 1;
2013-02-16 16:14:39 -08:00
}
2015-01-16 00:06:12 -06:00
/* Register Log System and Settings */
2019-05-16 00:12:21 -07:00
LogSys.SetGMSayHandler(&Zone::GMSayHookCallBackProcess);
2017-04-02 20:03:51 -07:00
database.LoadLogSettings(LogSys.log_settings);
LogSys.StartFileLogs();
2015-01-16 00:06:12 -06:00
2015-01-16 03:00:49 -06:00
/* Guilds */
2013-02-16 16:14:39 -08:00
guild_mgr.SetDatabase(&database);
2013-05-04 18:06:58 -07:00
GuildBanks = nullptr;
2013-02-16 16:14:39 -08:00
/**
* NPC Scale Manager
*/
npc_scale_manager = new NpcScaleManager;
npc_scale_manager->LoadScaleData();
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
2017-04-02 20:03:51 -07:00
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
2013-02-16 16:14:39 -08:00
#endif
2013-05-06 13:07:41 -04:00
2019-09-01 21:04:58 -05:00
LogInfo("CURRENT_VERSION: {}", CURRENT_VERSION);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
/*
2013-05-09 10:44:08 -04:00
* Setup nice signal handlers
*/
2017-04-02 20:03:51 -07:00
if (signal(SIGINT, CatchSignal) == SIG_ERR) {
2019-09-01 22:05:44 -05:00
LogError("Could not set signal handler");
return 1;
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
if (signal(SIGTERM, CatchSignal) == SIG_ERR) {
2019-09-01 22:05:44 -05:00
LogError("Could not set signal handler");
return 1;
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
#ifndef WIN32
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
2019-09-01 22:05:44 -05:00
LogError("Could not set signal handler");
return 1;
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
#endif
2013-02-16 16:14:39 -08:00
2019-09-01 21:04:58 -05:00
LogInfo("Mapping Incoming Opcodes");
2013-02-16 16:14:39 -08:00
MapOpcodes();
2019-09-01 21:04:58 -05:00
LogInfo("Loading Variables");
2013-02-16 16:14:39 -08:00
database.LoadVariables();
std::string hotfix_name;
2017-04-02 20:03:51 -07:00
if (database.GetVariable("hotfix_name", hotfix_name)) {
if (!hotfix_name.empty()) {
2019-09-01 21:04:58 -05:00
LogInfo("Current hotfix in use: [{}]", hotfix_name.c_str());
2015-06-23 22:04:48 -07:00
}
2015-06-23 17:39:06 -07:00
}
2019-09-01 21:04:58 -05:00
LogInfo("Loading zone names");
2013-02-16 16:14:39 -08:00
database.LoadZoneNames();
2019-09-01 21:04:58 -05:00
LogInfo("Loading items");
2017-04-02 20:03:51 -07:00
if (!database.LoadItems(hotfix_name)) {
2019-09-01 22:05:44 -05:00
LogError("Loading items failed!");
LogError("Failed. But ignoring error and going on..");
2013-02-16 16:14:39 -08:00
}
2019-09-01 21:04:58 -05:00
LogInfo("Loading npc faction lists");
2017-04-02 20:03:51 -07:00
if (!database.LoadNPCFactionLists(hotfix_name)) {
2019-09-01 22:05:44 -05:00
LogError("Loading npcs faction lists failed!");
return 1;
2013-02-16 16:14:39 -08:00
}
2019-09-01 21:04:58 -05:00
LogInfo("Loading loot tables");
2017-04-02 20:03:51 -07:00
if (!database.LoadLoot(hotfix_name)) {
2019-09-01 22:05:44 -05:00
LogError("Loading loot failed!");
return 1;
2013-05-09 10:44:08 -04:00
}
2019-09-01 21:04:58 -05:00
LogInfo("Loading skill caps");
2017-04-02 20:03:51 -07:00
if (!database.LoadSkillCaps(std::string(hotfix_name))) {
2019-09-01 22:05:44 -05:00
LogError("Loading skill caps failed!");
return 1;
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading spells");
2017-04-02 20:03:51 -07:00
if (!database.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) {
2019-09-01 22:05:44 -05:00
LogError("Loading spells failed!");
2015-06-23 17:39:06 -07:00
return 1;
}
2013-02-16 16:14:39 -08:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading base data");
2017-04-02 20:03:51 -07:00
if (!database.LoadBaseData(hotfix_name)) {
2019-09-01 22:05:44 -05:00
LogError("Loading base data failed!");
return 1;
}
2019-09-01 21:04:58 -05:00
LogInfo("Loading guilds");
2013-02-16 16:14:39 -08:00
guild_mgr.LoadGuilds();
2017-04-02 20:03:51 -07:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading factions");
2013-02-16 16:14:39 -08:00
database.LoadFactionData();
2017-04-02 20:03:51 -07:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading titles");
2013-02-16 16:14:39 -08:00
title_manager.LoadTitles();
2017-04-02 20:03:51 -07:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading tributes");
2013-02-16 16:14:39 -08:00
database.LoadTributes();
2017-04-02 20:03:51 -07:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading corpse timers");
2013-02-16 16:14:39 -08:00
database.GetDecayTimes(npcCorpseDecayTimes);
2017-04-02 20:03:51 -07:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading profanity list");
if (!EQ::ProfanityManager::LoadProfanityList(&database))
2019-09-01 22:05:44 -05:00
LogError("Loading profanity list failed!");
2019-02-04 07:02:27 -05:00
2019-09-01 21:04:58 -05:00
LogInfo("Loading commands");
2017-04-02 20:03:51 -07:00
int retval = command_init();
if (retval<0)
2019-09-01 22:05:44 -05:00
LogError("Command loading failed");
2013-02-16 16:14:39 -08:00
else
2019-09-01 21:04:58 -05:00
LogInfo("{} commands loaded", retval);
2013-02-16 16:14:39 -08:00
//rules:
{
std::string tmp;
if (database.GetVariable("RuleSet", tmp)) {
2019-09-01 21:04:58 -05:00
LogInfo("Loading rule set [{}]", tmp.c_str());
if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) {
2019-09-01 22:05:44 -05:00
LogError("Failed to load ruleset [{}], falling back to defaults", tmp.c_str());
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
}
else {
if (!RuleManager::Instance()->LoadRules(&database, "default", false)) {
2019-09-01 21:04:58 -05:00
LogInfo("No rule set configured, using default rules");
2017-04-02 20:03:51 -07:00
}
else {
2019-09-01 21:04:58 -05:00
LogInfo("Loaded default rule set 'default'");
2013-02-16 16:14:39 -08:00
}
}
2019-01-12 20:03:52 -05:00
EQ::InitializeDynamicLookups();
2019-09-01 21:04:58 -05:00
LogInfo("Initialized dynamic dictionary entries");
2013-02-16 16:14:39 -08:00
}
#ifdef BOTS
2019-09-02 01:39:52 -05:00
LogInfo("Loading bot commands");
int botretval = bot_command_init();
if (botretval<0)
2019-09-01 22:05:44 -05:00
LogError("Bot command loading failed");
else
2019-09-02 01:39:52 -05:00
LogInfo("[{}] bot commands loaded", botretval);
2019-09-02 01:39:52 -05:00
LogInfo("Loading bot spell casting chances");
if (!database.botdb.LoadBotSpellCastingChances())
2019-09-01 22:05:44 -05:00
LogError("Bot spell casting chances loading failed");
#endif
2017-04-02 20:03:51 -07:00
if (RuleB(TaskSystem, EnableTaskSystem)) {
Log(Logs::General, Logs::Tasks, "[INIT] Loading Tasks");
2013-02-16 16:14:39 -08:00
taskmanager = new TaskManager;
taskmanager->LoadTasks();
}
2013-05-09 10:44:08 -04:00
parse = new QuestParserCollection();
#ifdef LUA_EQEMU
2017-04-22 16:40:01 -07:00
parse->RegisterQuestInterface(LuaParser::Instance(), "lua");
#endif
#ifdef EMBPERL
2016-05-25 16:10:28 -04:00
auto perl_parser = new PerlembParser();
parse->RegisterQuestInterface(perl_parser, "pl");
/* Load Perl Event Export Settings */
parse->LoadPerlEventExportSettings(parse->perl_event_export_settings);
#endif
2013-02-16 16:14:39 -08:00
//now we have our parser, load the quests
2019-09-01 21:04:58 -05:00
LogInfo("Loading quests");
2013-02-16 16:14:39 -08:00
parse->ReloadQuests();
2013-05-06 13:07:41 -04:00
worldserver.Connect();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect
#ifdef EQPROFILE
#ifdef PROFILE_DUMP_TIME
2017-04-02 20:03:51 -07:00
Timer profile_dump_timer(PROFILE_DUMP_TIME * 1000);
2013-02-16 16:14:39 -08:00
profile_dump_timer.Start();
#endif
#endif
2017-04-02 20:03:51 -07:00
if (!strlen(zone_name) || !strcmp(zone_name, ".")) {
2019-09-01 21:04:58 -05:00
LogInfo("Entering sleep mode");
2017-04-02 20:03:51 -07:00
}
else if (!Zone::Bootup(database.GetZoneID(zone_name), instance_id, true)) {
2019-09-01 22:05:44 -05:00
LogError("Zone Bootup failed :: Zone::Bootup");
2013-02-16 16:14:39 -08:00
zone = 0;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//register all the patches we have avaliable with the stream identifier.
EQStreamIdentifier stream_identifier;
RegisterAllPatches(stream_identifier);
2013-05-06 13:07:41 -04:00
#ifdef __linux__
2019-09-01 23:54:24 -05:00
LogDebug("Main thread running with thread id [{}]", pthread_self());
#elif defined(__FreeBSD__)
LogDebug("Main thread running with thread id [{}]", pthread_getthreadid_np());
2013-02-16 16:14:39 -08:00
#endif
2013-05-06 13:07:41 -04:00
bool worldwasconnected = worldserver.Connected();
bool eqsf_open = false;
2019-06-23 21:33:24 -05:00
bool websocker_server_opened = false;
2019-03-09 23:08:38 -06:00
2013-02-16 16:14:39 -08:00
Timer quest_timers(100);
2019-10-13 18:49:16 -07:00
UpdateWindowTitle(nullptr);
std::shared_ptr<EQStreamInterface> eqss;
2013-02-16 16:14:39 -08:00
EQStreamInterface *eqsi;
std::unique_ptr<EQ::Net::EQStreamManager> eqsm;
std::chrono::time_point<std::chrono::system_clock> frame_prev = std::chrono::system_clock::now();
2019-05-16 00:12:21 -07:00
std::unique_ptr<EQ::Net::WebsocketServer> ws_server;
auto loop_fn = [&](EQ::Timer* t) {
2013-02-16 16:14:39 -08:00
//Advance the timer to our current point in time
Timer::SetCurrentTime();
2013-05-06 13:07:41 -04:00
2019-03-09 23:08:38 -06:00
/**
* Calculate frame time
*/
2017-04-02 20:03:51 -07:00
std::chrono::time_point<std::chrono::system_clock> frame_now = std::chrono::system_clock::now();
frame_time = std::chrono::duration_cast<std::chrono::duration<double>>(frame_now - frame_prev).count();
2017-04-02 20:03:51 -07:00
frame_prev = frame_now;
2019-03-09 23:08:38 -06:00
/**
2019-09-02 03:30:03 -05:00
* Websocket server
2019-03-09 23:08:38 -06:00
*/
2019-06-23 21:33:24 -05:00
if (!websocker_server_opened && Config->ZonePort != 0) {
2019-09-02 03:30:03 -05:00
LogInfo("Websocket Server listener started ([{}]:[{}])", Config->TelnetIP.c_str(), Config->ZonePort);
2019-06-23 21:33:24 -05:00
ws_server.reset(new EQ::Net::WebsocketServer(Config->TelnetIP, Config->ZonePort));
RegisterApiService(ws_server);
websocker_server_opened = true;
2019-03-09 23:08:38 -06:00
}
/**
* EQStreamManager
*/
2017-04-02 20:03:51 -07:00
if (!eqsf_open && Config->ZonePort != 0) {
2019-09-01 21:04:58 -05:00
LogInfo("Starting EQ Network server on port {}", Config->ZonePort);
2013-05-06 13:07:41 -04:00
2019-04-16 01:09:25 -07:00
EQStreamManagerInterfaceOptions opts(Config->ZonePort, false, RuleB(Network, CompressZoneStream));
opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor);
opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS);
opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS);
opts.daybreak_options.outgoing_data_rate = RuleR(Network, ClientDataRate);
2017-04-02 20:03:51 -07:00
eqsm.reset(new EQ::Net::EQStreamManager(opts));
eqsf_open = true;
eqsm->OnNewConnection([&stream_identifier](std::shared_ptr<EQ::Net::EQStream> stream) {
stream_identifier.AddStream(stream);
LogF(Logs::Detail, Logs::WorldServer, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort()));
2017-04-02 20:03:51 -07:00
});
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//give the stream identifier a chance to do its work....
stream_identifier.Process();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
//check the stream identifier for any now-identified streams
2017-04-02 20:03:51 -07:00
while ((eqsi = stream_identifier.PopIdentified())) {
2013-02-16 16:14:39 -08:00
//now that we know what patch they are running, start up their client object
struct in_addr in;
in.s_addr = eqsi->GetRemoteIP();
LogInfo("New client from [{}]:[{}]", inet_ntoa(in), ntohs(eqsi->GetRemotePort()));
2016-05-25 16:10:28 -04:00
auto client = new Client(eqsi);
2013-02-16 16:14:39 -08:00
entity_list.AddClient(client);
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (worldserver.Connected()) {
worldwasconnected = true;
}
else {
2017-04-09 20:17:48 -07:00
if (worldwasconnected && is_zone_loaded) {
entity_list.ChannelMessageFromWorld(0, 0, ChatChannel_Broadcast, 0, 0, 100, "WARNING: World server connection lost");
2017-04-09 20:17:48 -07:00
worldwasconnected = false;
}
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
if (is_zone_loaded) {
2013-02-16 16:14:39 -08:00
{
2019-10-13 18:49:16 -07:00
entity_list.GroupProcess();
entity_list.DoorProcess();
entity_list.ObjectProcess();
entity_list.CorpseProcess();
entity_list.TrapProcess();
entity_list.RaidProcess();
2017-04-02 20:03:51 -07:00
entity_list.Process();
entity_list.MobProcess();
entity_list.BeaconProcess();
entity_list.EncounterProcess();
if (zone) {
2017-04-02 20:03:51 -07:00
if (!zone->Process()) {
Zone::Shutdown();
2013-02-16 16:14:39 -08:00
}
}
if (quest_timers.Check()) {
quest_manager.Process();
}
}
2013-02-16 16:14:39 -08:00
}
2017-04-02 20:03:51 -07:00
if (InterserverTimer.Check()) {
InterserverTimer.Start();
database.ping();
entity_list.UpdateWho();
2013-02-16 16:14:39 -08:00
}
};
EQ::Timer process_timer(loop_fn);
process_timer.Start(32, true);
EQ::EventLoop::Get().Run();
2013-02-16 16:14:39 -08:00
entity_list.Clear();
entity_list.RemoveAllEncounters(); // gotta do it manually or rewrite lots of shit :P
parse->ClearInterfaces();
2013-05-02 23:21:04 -07:00
#ifdef EMBPERL
2017-04-02 20:03:51 -07:00
safe_delete(perl_parser);
2013-05-02 23:21:04 -07:00
#endif
safe_delete(Config);
2013-05-06 13:07:41 -04:00
if (zone != 0)
2013-02-16 16:14:39 -08:00
Zone::Shutdown(true);
//Fix for Linux world server problem.
safe_delete(taskmanager);
command_deinit();
#ifdef BOTS
bot_command_deinit();
#endif
safe_delete(parse);
2019-09-01 21:04:58 -05:00
LogInfo("Proper zone shutdown complete.");
LogSys.CloseFileLogs();
2013-02-16 16:14:39 -08:00
return 0;
}
void Shutdown()
{
Zone::Shutdown(true);
2019-09-01 21:04:58 -05:00
LogInfo("Shutting down...");
LogSys.CloseFileLogs();
2020-03-21 17:05:28 -07:00
EQ::EventLoop::Get().Shutdown();
}
void CatchSignal(int sig_num) {
#ifdef _WINDOWS
LogInfo("Recieved signal: [{}]", sig_num);
#endif
Shutdown();
2013-02-16 16:14:39 -08:00
}
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload intensive or metric based logging to a remote server process that could exist on another server entirely Implemented Player Event Logging Types (Go to table `qs_player_events`): 1 = Player_Log_Quest, 2 = Player_Log_Zoning, 3 = Player_Log_Deaths, 4 = Player_Log_Connect_State, 5 = Player_Log_Levels, 6 = Player_Log_Keyring_Addition, 7 = Player_Log_QGlobal_Update, 8 = Player_Log_Task_Updates, 9 = Player_Log_AA_Purchases, 10 = Player_Log_Trade_Skill_Events, 11 = Player_Log_Issued_Commands, 12 = Player_Log_Money_Transactions, 13 = Player_Log_Alternate_Currency_Transactions, - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon Changed all QS Error related logging to 'QUERYSERV__ERROR' (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( Implemented Perl Quest objects (LUA still needed to be exported): - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` Added MySQL Tables: - `qs_player_aa_rate_hourly` - `qs_player_events` - Source table structures from: - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly To get the complete QueryServ schema, source from here: - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql Added rules for each logging type, source rules here with them enabled by default: - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql Spawn related logging cleanup General code cleanup Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
/* Update Window Title with relevant information */
2013-02-16 16:14:39 -08:00
void UpdateWindowTitle(char* iNewTitle) {
#ifdef _WINDOWS
char tmp[500];
if (iNewTitle) {
snprintf(tmp, sizeof(tmp), "%i: %s", ZoneConfig::get()->ZonePort, iNewTitle);
}
else {
if (zone) {
2017-04-02 20:03:51 -07:00
#if defined(GOTFRAGS) || defined(_EQDEBUG)
snprintf(tmp, sizeof(tmp), "%i: %s, %i clients, %i", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients, getpid());
#else
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload intensive or metric based logging to a remote server process that could exist on another server entirely Implemented Player Event Logging Types (Go to table `qs_player_events`): 1 = Player_Log_Quest, 2 = Player_Log_Zoning, 3 = Player_Log_Deaths, 4 = Player_Log_Connect_State, 5 = Player_Log_Levels, 6 = Player_Log_Keyring_Addition, 7 = Player_Log_QGlobal_Update, 8 = Player_Log_Task_Updates, 9 = Player_Log_AA_Purchases, 10 = Player_Log_Trade_Skill_Events, 11 = Player_Log_Issued_Commands, 12 = Player_Log_Money_Transactions, 13 = Player_Log_Alternate_Currency_Transactions, - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon Changed all QS Error related logging to 'QUERYSERV__ERROR' (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( Implemented Perl Quest objects (LUA still needed to be exported): - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` Added MySQL Tables: - `qs_player_aa_rate_hourly` - `qs_player_events` - Source table structures from: - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly To get the complete QueryServ schema, source from here: - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql Added rules for each logging type, source rules here with them enabled by default: - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql Spawn related logging cleanup General code cleanup Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
snprintf(tmp, sizeof(tmp), "%s :: clients: %i inst_id: %i inst_ver: %i :: port: %i", zone->GetShortName(), numclients, zone->GetInstanceID(), zone->GetInstanceVersion(), ZoneConfig::get()->ZonePort);
2017-04-02 20:03:51 -07:00
#endif
2013-02-16 16:14:39 -08:00
}
else {
2017-04-02 20:03:51 -07:00
#if defined(GOTFRAGS) || defined(_EQDEBUG)
snprintf(tmp, sizeof(tmp), "%i: sleeping, %i", ZoneConfig::get()->ZonePort, getpid());
#else
snprintf(tmp, sizeof(tmp), "%i: sleeping", ZoneConfig::get()->ZonePort);
#endif
2013-02-16 16:14:39 -08:00
}
}
SetConsoleTitle(tmp);
#endif
}