stats flush_metrics(const storage& storage, unsigned int period_ms) { stats stats; stats.timestamp = timer::now(); auto period = period_ms / 1000.0; FOR_EACH (auto& c, storage.counters) stats.counters[c.first] = c.second / period; FOR_EACH (auto& g, storage.gauges) stats.gauges[g.first] = g.second; FOR_EACH (auto& t, storage.timers) stats.timers[t.first] = process_timer(t.first, t.second); return stats; // todo: move }
void serve(void) { int32_t reqno; uint32_t whom; int i, perm; void *va; while (1) { // ipc_recv will block the entire process, so we flush // all pending work from other threads. We limit the // number of yields in case there's a rogue thread. for (i = 0; thread_wakeups_pending() && i < 32; ++i) thread_yield(); perm = 0; va = get_buffer(); reqno = ipc_recv((int32_t *) &whom, (void *) va, &perm); if (debug) { cprintf("ns req %d from %08x\n", reqno, whom); } // first take care of requests that do not contain an argument page if (reqno == NSREQ_TIMER) { process_timer(whom); put_buffer(va); continue; } // All remaining requests must contain an argument page if (!(perm & PTE_P)) { cprintf("Invalid request from %08x: no argument page\n", whom); continue; // just leave it hanging... } // Since some lwIP socket calls will block, create a thread and // process the rest of the request in the thread. struct st_args *args = malloc(sizeof(struct st_args)); if (!args) panic("could not allocate thread args structure"); args->reqno = reqno; args->whom = whom; args->req = va; thread_create(0, "serve_thread", serve_thread, (uint32_t)args); thread_yield(); // let the thread created run } }
static void process_timer_list(egg_timer_t* &timer_list) { egg_timer_t *timer = NULL, *prev = NULL, *next = timer_list; while (next) { timer = next; // Timers are sorted by lowest->highest, so if the current one isn't ready to trigger, the rest are not either if (timer->trigger_time.sec > now.sec || (timer->trigger_time.sec == now.sec && timer->trigger_time.usec > now.usec)) break; next = timer->next; if (process_timer(timer)) { // Deleted, need to shift the queue if (prev) prev->next = timer->next; else timer_list = timer->next; if (timer->name) free(timer->name); free(timer); } else prev = timer; } }
NLog::NLog( int _buf_max ) { buf_count_ = 0; last_tick_ = msec(); time_len_ = 0; bzero( time_str_, sizeof( time_str_ ) ); bzero( time_file_name_, sizeof( time_file_name_ ) ); pthread_mutexattr_t ma; pthread_mutexattr_init( &ma ); pthread_mutexattr_settype( &ma, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init(&mutex_, &ma); pthread_mutexattr_destroy( &ma ); pthread_mutexattr_init( &ma ); pthread_mutexattr_settype( &ma, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init(&buf_count_mtx_, &ma); pthread_mutexattr_destroy( &ma ); olog_fn_ = 0; olog_ln_ = 0; daemon_mode_ = 0; bzero( log_path_, sizeof( log_path_ ) ); bzero( event_log_path_, sizeof( event_log_path_ ) ); bzero( log_, sizeof( log_ ) ); last_mod_min_ = -1; switch_flag_ = 0; INIT_LIST_HEAD( &write_list_ ); INIT_LIST_HEAD( &free_list_ ); buf_mem_ = new BufferNode[_buf_max]; buf_max_ = _buf_max; buf_new_ = 0; for ( int i = 0; i < _buf_max; ++i ){ list_add_tail( &(buf_mem_[i].link_), &free_list_ ); } process_timer(); }
int main(int argc, char** argv) { RegisterExecutablePlatform(ExePlatformZone); LogSys.LoadLogSettingsDefaults(); 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; return 0; } #endif /*USE_MAP_MMFS*/ QServ = new QueryServ; Log(Logs::General, Logs::Zone_Server, "Loading server configuration.."); if (!ZoneConfig::LoadConfig()) { Log(Logs::General, Logs::Error, "Loading server configuration failed."); return 1; } Config = ZoneConfig::get(); const char *zone_name; uint32 instance_id = 0; std::string z_name; if (argc == 4) { instance_id = atoi(argv[3]); worldserver.SetLauncherName(argv[2]); auto zone_port = SplitString(argv[1], ':'); if (!zone_port.empty()) { z_name = zone_port[0]; } 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()); if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) { zone_name = "."; } else { zone_name = z_name.c_str(); } } else if (argc == 3) { worldserver.SetLauncherName(argv[2]); auto zone_port = SplitString(argv[1], ':'); if (!zone_port.empty()) { z_name = zone_port[0]; } 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()); if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) { zone_name = "."; } else { zone_name = z_name.c_str(); } } else if (argc == 2) { worldserver.SetLauncherName("NONE"); auto zone_port = SplitString(argv[1], ':'); if (!zone_port.empty()) { z_name = zone_port[0]; } 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()); if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) { zone_name = "."; } else { zone_name = z_name.c_str(); } } else { zone_name = "."; worldserver.SetLaunchedName("."); worldserver.SetLauncherName("NONE"); } Log(Logs::General, Logs::Zone_Server, "Connecting to MySQL..."); if (!database.Connect( Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { Log(Logs::General, Logs::Error, "Cannot continue without a database connection."); return 1; } #ifdef BOTS if (!botdb.Connect( Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { Log(Logs::General, Logs::Error, "Cannot continue without a bots database connection."); return 1; } #endif /* Register Log System and Settings */ LogSys.OnLogHookCallBackZone(&Zone::GMSayHookCallBackProcess); database.LoadLogSettings(LogSys.log_settings); LogSys.StartFileLogs(); /* Guilds */ guild_mgr.SetDatabase(&database); GuildBanks = nullptr; /** * NPC Scale Manager */ npc_scale_manager = new NpcScaleManager; npc_scale_manager->LoadScaleData(); #ifdef _EQDEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif Log(Logs::General, Logs::Zone_Server, "CURRENT_VERSION: %s", CURRENT_VERSION); /* * Setup nice signal handlers */ if (signal(SIGINT, CatchSignal) == SIG_ERR) { Log(Logs::General, Logs::Error, "Could not set signal handler"); return 1; } if (signal(SIGTERM, CatchSignal) == SIG_ERR) { Log(Logs::General, Logs::Error, "Could not set signal handler"); return 1; } #ifndef WIN32 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { Log(Logs::General, Logs::Error, "Could not set signal handler"); return 1; } #endif Log(Logs::General, Logs::Zone_Server, "Mapping Incoming Opcodes"); MapOpcodes(); Log(Logs::General, Logs::Zone_Server, "Loading Variables"); database.LoadVariables(); std::string hotfix_name; if (database.GetVariable("hotfix_name", hotfix_name)) { if (!hotfix_name.empty()) { Log(Logs::General, Logs::Zone_Server, "Current hotfix in use: '%s'", hotfix_name.c_str()); } } Log(Logs::General, Logs::Zone_Server, "Loading zone names"); database.LoadZoneNames(); Log(Logs::General, Logs::Zone_Server, "Loading items"); if (!database.LoadItems(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading items FAILED!"); Log(Logs::General, Logs::Error, "Failed. But ignoring error and going on..."); } Log(Logs::General, Logs::Zone_Server, "Loading npc faction lists"); if (!database.LoadNPCFactionLists(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading npcs faction lists FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading loot tables"); if (!database.LoadLoot(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading loot FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading skill caps"); if (!database.LoadSkillCaps(std::string(hotfix_name))) { Log(Logs::General, Logs::Error, "Loading skill caps FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading spells"); if (!database.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) { Log(Logs::General, Logs::Error, "Loading spells FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading base data"); if (!database.LoadBaseData(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading base data FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading guilds"); guild_mgr.LoadGuilds(); Log(Logs::General, Logs::Zone_Server, "Loading factions"); database.LoadFactionData(); Log(Logs::General, Logs::Zone_Server, "Loading titles"); title_manager.LoadTitles(); Log(Logs::General, Logs::Zone_Server, "Loading tributes"); database.LoadTributes(); Log(Logs::General, Logs::Zone_Server, "Loading corpse timers"); database.GetDecayTimes(npcCorpseDecayTimes); Log(Logs::General, Logs::Zone_Server, "Loading profanity list"); if (!EQEmu::ProfanityManager::LoadProfanityList(&database)) Log(Logs::General, Logs::Error, "Loading profanity list FAILED!"); Log(Logs::General, Logs::Zone_Server, "Loading commands"); int retval = command_init(); if (retval<0) Log(Logs::General, Logs::Error, "Command loading FAILED"); else Log(Logs::General, Logs::Zone_Server, "%d commands loaded", retval); //rules: { std::string tmp; if (database.GetVariable("RuleSet", tmp)) { Log(Logs::General, Logs::Zone_Server, "Loading rule set '%s'", tmp.c_str()); if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) { Log(Logs::General, Logs::Error, "Failed to load ruleset '%s', falling back to defaults.", tmp.c_str()); } } else { if (!RuleManager::Instance()->LoadRules(&database, "default", false)) { Log(Logs::General, Logs::Zone_Server, "No rule set configured, using default rules"); } else { Log(Logs::General, Logs::Zone_Server, "Loaded default rule set 'default'", tmp.c_str()); } } EQEmu::InitializeDynamicLookups(); Log(Logs::General, Logs::Zone_Server, "Initialized dynamic dictionary entries"); } #ifdef BOTS Log(Logs::General, Logs::Zone_Server, "Loading bot commands"); int botretval = bot_command_init(); if (botretval<0) Log(Logs::General, Logs::Error, "Bot command loading FAILED"); else Log(Logs::General, Logs::Zone_Server, "%d bot commands loaded", botretval); Log(Logs::General, Logs::Zone_Server, "Loading bot spell casting chances"); if (!botdb.LoadBotSpellCastingChances()) Log(Logs::General, Logs::Error, "Bot spell casting chances loading FAILED"); #endif if (RuleB(TaskSystem, EnableTaskSystem)) { Log(Logs::General, Logs::Tasks, "[INIT] Loading Tasks"); taskmanager = new TaskManager; taskmanager->LoadTasks(); } parse = new QuestParserCollection(); #ifdef LUA_EQEMU parse->RegisterQuestInterface(LuaParser::Instance(), "lua"); #endif #ifdef EMBPERL auto perl_parser = new PerlembParser(); parse->RegisterQuestInterface(perl_parser, "pl"); /* Load Perl Event Export Settings */ parse->LoadPerlEventExportSettings(parse->perl_event_export_settings); #endif //now we have our parser, load the quests Log(Logs::General, Logs::Zone_Server, "Loading quests"); parse->ReloadQuests(); worldserver.Connect(); Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect #ifdef EQPROFILE #ifdef PROFILE_DUMP_TIME Timer profile_dump_timer(PROFILE_DUMP_TIME * 1000); profile_dump_timer.Start(); #endif #endif if (!strlen(zone_name) || !strcmp(zone_name, ".")) { Log(Logs::General, Logs::Zone_Server, "Entering sleep mode"); } else if (!Zone::Bootup(database.GetZoneID(zone_name), instance_id, true)) { Log(Logs::General, Logs::Error, "Zone Bootup failed :: Zone::Bootup"); zone = 0; } //register all the patches we have avaliable with the stream identifier. EQStreamIdentifier stream_identifier; RegisterAllPatches(stream_identifier); #ifndef WIN32 Log(Logs::Detail, Logs::None, "Main thread running with thread id %d", pthread_self()); #endif Timer quest_timers(100); UpdateWindowTitle(); bool worldwasconnected = worldserver.Connected(); std::shared_ptr<EQStreamInterface> eqss; EQStreamInterface *eqsi; bool eqsf_open = false; std::unique_ptr<EQ::Net::EQStreamManager> eqsm; std::chrono::time_point<std::chrono::system_clock> frame_prev = std::chrono::system_clock::now(); auto loop_fn = [&](EQ::Timer* t) { //Advance the timer to our current point in time Timer::SetCurrentTime(); //Calculate frame time 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(); frame_prev = frame_now; if (!eqsf_open && Config->ZonePort != 0) { Log(Logs::General, Logs::Zone_Server, "Starting EQ Network server on port %d", Config->ZonePort); EQ::Net::EQStreamManagerOptions opts(Config->ZonePort, false, true); 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); 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::World_Server, "New connection from IP {0}:{1}", stream->RemoteEndpoint(), ntohs(stream->GetRemotePort())); }); } //give the stream identifier a chance to do its work.... stream_identifier.Process(); //check the stream identifier for any now-identified streams while ((eqsi = stream_identifier.PopIdentified())) { //now that we know what patch they are running, start up their client object struct in_addr in; in.s_addr = eqsi->GetRemoteIP(); Log(Logs::Detail, Logs::World_Server, "New client from %s:%d", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); auto client = new Client(eqsi); entity_list.AddClient(client); } if (worldserver.Connected()) { worldwasconnected = true; } else { if (worldwasconnected && is_zone_loaded) { entity_list.ChannelMessageFromWorld(0, 0, 6, 0, 0, "WARNING: World server connection lost"); worldwasconnected = false; } } if (is_zone_loaded) { { if (net.group_timer.Enabled() && net.group_timer.Check()) entity_list.GroupProcess(); if (net.door_timer.Enabled() && net.door_timer.Check()) entity_list.DoorProcess(); if (net.object_timer.Enabled() && net.object_timer.Check()) entity_list.ObjectProcess(); if (net.corpse_timer.Enabled() && net.corpse_timer.Check()) entity_list.CorpseProcess(); if (net.trap_timer.Enabled() && net.trap_timer.Check()) entity_list.TrapProcess(); if (net.raid_timer.Enabled() && net.raid_timer.Check()) entity_list.RaidProcess(); entity_list.Process(); entity_list.MobProcess(); entity_list.BeaconProcess(); entity_list.EncounterProcess(); if (zone) { if (!zone->Process()) { Zone::Shutdown(); } } if (quest_timers.Check()) quest_manager.Process(); } } if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); entity_list.UpdateWho(); } }; EQ::Timer process_timer(loop_fn); bool is_boat_zone = strstr(zone_name, "erudnext") != NULL || strstr(zone_name, "freporte") != NULL || strstr(zone_name, "qeynos") != NULL || strstr(zone_name, "oot") != NULL || strstr(zone_name, "timorous") != NULL || strstr(zone_name, "erudsxing") != NULL || strstr(zone_name, "firiona") != NULL || strstr(zone_name, "butcher") != NULL || strstr(zone_name, "overthere") != NULL || strstr(zone_name, "oasis") != NULL || strstr(zone_name, "nro") != NULL || strstr(zone_name, "iceclad") != NULL; if (!is_boat_zone) process_timer.Start(1000, true); else process_timer.Start(100, true); while (RunLoops) { if (!is_boat_zone) { bool previous_loaded = is_zone_loaded && numclients > 0; EQ::EventLoop::Get().Process(); bool current_loaded = is_zone_loaded && numclients > 0; if (previous_loaded && !current_loaded) { process_timer.Stop(); process_timer.Start(1000, true); } else if (!previous_loaded && current_loaded) { process_timer.Stop(); process_timer.Start(32, true); } if (current_loaded) { Sleep(1); } else { Sleep(10); } } else { bool previous_loaded = is_zone_loaded; EQ::EventLoop::Get().Process(); bool current_loaded = is_zone_loaded; if (previous_loaded && !current_loaded) { process_timer.Stop(); process_timer.Start(100, true); if (zone && zone->GetZoneID() && zone->GetInstanceVersion()) { uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion()); zone->StartShutdownTimer(shutdown_timer); } } else if (!previous_loaded && current_loaded) { process_timer.Stop(); process_timer.Start(10, true); } Sleep(1); } } entity_list.Clear(); entity_list.RemoveAllEncounters(); // gotta do it manually or rewrite lots of shit :P parse->ClearInterfaces(); #ifdef EMBPERL safe_delete(perl_parser); #endif safe_delete(Config); if (zone != 0) Zone::Shutdown(true); //Fix for Linux world server problem. safe_delete(taskmanager); command_deinit(); #ifdef BOTS bot_command_deinit(); #endif safe_delete(parse); Log(Logs::General, Logs::Zone_Server, "Proper zone shutdown complete."); LogSys.CloseFileLogs(); return 0; }