mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
[API] Implement Zone Sidecar (#3635)
* Zone sidecar work * Process management work * Merge * Sidecar work * API config option * Request proxy work * Proxy headers and params * Change port * Remove code * Sim work * Sidecar work * Update loot_simulator_controller.cpp * Update loot_simulator_controller.cpp * Formatting * Post merge change * Windows compile fix * Update sidecar_api.cpp * Update strings.cpp
This commit is contained in:
parent
0bbfcf7adc
commit
b027edd21e
@ -70,6 +70,7 @@ SET(common_sources
|
||||
perl_eqdb.cpp
|
||||
perl_eqdb_res.cpp
|
||||
process/process.cpp
|
||||
process.cpp
|
||||
proc_launcher.cpp
|
||||
profanity_manager.cpp
|
||||
ptimer.cpp
|
||||
@ -90,6 +91,7 @@ SET(common_sources
|
||||
timer.cpp
|
||||
unix.cpp
|
||||
platform.cpp
|
||||
json/json.hpp
|
||||
json/jsoncpp.cpp
|
||||
zone_store.cpp
|
||||
net/console_server.cpp
|
||||
@ -583,6 +585,7 @@ SET(common_headers
|
||||
path_manager.cpp
|
||||
platform.h
|
||||
process/process.h
|
||||
process.h
|
||||
proc_launcher.h
|
||||
profanity_manager.h
|
||||
profiler.h
|
||||
|
||||
@ -39,15 +39,15 @@ namespace EQEmuCommand {
|
||||
{
|
||||
if (cmd[{"-d", "--debug"}]) {
|
||||
std::cout << "Positional args:\n";
|
||||
for (auto &pos_arg : cmd.pos_args())
|
||||
for (auto &pos_arg: cmd.pos_args())
|
||||
std::cout << '\t' << pos_arg << std::endl;
|
||||
|
||||
std::cout << "\nFlags:\n";
|
||||
for (auto &flag : cmd.flags())
|
||||
for (auto &flag: cmd.flags())
|
||||
std::cout << '\t' << flag << std::endl;
|
||||
|
||||
std::cout << "\nParameters:\n";
|
||||
for (auto ¶m : cmd.params())
|
||||
for (auto ¶m: cmd.params())
|
||||
std::cout << '\t' << param.first << " : " << param.second << std::endl;
|
||||
}
|
||||
}
|
||||
@ -69,8 +69,8 @@ namespace EQEmuCommand {
|
||||
{
|
||||
bool arguments_filled = true;
|
||||
|
||||
int index = 2;
|
||||
for (auto &arg : arguments) {
|
||||
int index = 2;
|
||||
for (auto &arg: arguments) {
|
||||
if (cmd(arg).str().empty() && cmd(index).str().empty()) {
|
||||
arguments_filled = false;
|
||||
}
|
||||
@ -79,12 +79,12 @@ namespace EQEmuCommand {
|
||||
|
||||
if (!arguments_filled || (argc == 2 && !cmd[{"-h", "--help"}]) || (argc == 3 && cmd[{"-h", "--help"}])) {
|
||||
std::string arguments_string;
|
||||
for (auto &arg : arguments) {
|
||||
for (auto &arg: arguments) {
|
||||
arguments_string += " " + arg;
|
||||
}
|
||||
|
||||
std::string options_string;
|
||||
for (auto &opt : options) {
|
||||
for (auto &opt: options) {
|
||||
options_string += " " + opt + "\n";
|
||||
}
|
||||
|
||||
@ -124,14 +124,6 @@ namespace EQEmuCommand {
|
||||
)
|
||||
{
|
||||
std::string description;
|
||||
bool ran_command = false;
|
||||
for (auto &it: in_function_map) {
|
||||
if (it.first == argv[1]) {
|
||||
(it.second)(argc, argv, cmd, description);
|
||||
ran_command = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd[{"-h", "--help"}]) {
|
||||
std::cout << std::endl;
|
||||
std::cout <<
|
||||
@ -142,9 +134,7 @@ namespace EQEmuCommand {
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
|
||||
/**
|
||||
* Get max command length for padding length
|
||||
*/
|
||||
// Get max command length for padding length
|
||||
int max_command_length = 0;
|
||||
|
||||
for (auto &it: in_function_map) {
|
||||
@ -155,18 +145,14 @@ namespace EQEmuCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display command menu
|
||||
*/
|
||||
// Display command menu
|
||||
std::string command_section;
|
||||
for (auto &it: in_function_map) {
|
||||
description.clear();
|
||||
|
||||
(it.second)(argc, argv, cmd, description);
|
||||
|
||||
/**
|
||||
* Print section header
|
||||
*/
|
||||
// Print section header
|
||||
std::string command_prefix = it.first.substr(0, it.first.find(":"));
|
||||
|
||||
if (command_prefix.find("test") != std::string::npos) {
|
||||
@ -178,9 +164,7 @@ namespace EQEmuCommand {
|
||||
std::cout << termcolor::reset << command_prefix << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print commands
|
||||
*/
|
||||
// Print commands
|
||||
std::stringstream command;
|
||||
command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset;
|
||||
printf(" %-*s %s\n", max_command_length, command.str().c_str(), description.c_str());
|
||||
@ -191,6 +175,15 @@ namespace EQEmuCommand {
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
bool ran_command = false;
|
||||
|
||||
for (auto &it: in_function_map) {
|
||||
if (it.first == argv[1]) {
|
||||
(it.second)(argc, argv, cmd, description);
|
||||
ran_command = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ran_command) {
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
24640
common/json/json.hpp
Normal file
24640
common/json/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -72,6 +72,8 @@ std::string GetPlatformName()
|
||||
return "HC";
|
||||
case EQEmuExePlatform::ExePlatformTests:
|
||||
return "Tests";
|
||||
case EQEmuExePlatform::ExePlatformZoneSidecar:
|
||||
return "ZoneSidecar";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -37,7 +37,8 @@ enum EQEmuExePlatform
|
||||
ExePlatformClientImport,
|
||||
ExePlatformClientExport,
|
||||
ExePlatformHC,
|
||||
ExePlatformTests
|
||||
ExePlatformTests,
|
||||
ExePlatformZoneSidecar
|
||||
};
|
||||
|
||||
void RegisterExecutablePlatform(EQEmuExePlatform p);
|
||||
|
||||
44
common/process.cpp
Normal file
44
common/process.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include "process.h"
|
||||
|
||||
inline std::string random_string(size_t length)
|
||||
{
|
||||
auto randchar = []() -> char {
|
||||
const char charset[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[static_cast<size_t>(std::rand()) % max_index];
|
||||
};
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string Process::execute(const std::string &cmd, bool return_result)
|
||||
{
|
||||
std::string random = "/tmp/" + random_string(25);
|
||||
const char *file_name = random.c_str();
|
||||
|
||||
if (return_result) {
|
||||
#ifdef _WINDOWS
|
||||
std::system((cmd + " > " + file_name + " 2>&1").c_str());
|
||||
#else
|
||||
std::system((cmd + " > " + file_name + " 2>&1").c_str());
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
std::system((cmd).c_str());
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
if (return_result) {
|
||||
std::ifstream file(file_name);
|
||||
result = {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
||||
std::remove(file_name);
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
10
common/process.h
Normal file
10
common/process.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef EQEMU_PROCESS_H
|
||||
#define EQEMU_PROCESS_H
|
||||
|
||||
class Process {
|
||||
public:
|
||||
static std::string execute(const std::string &cmd, bool return_result = true);
|
||||
};
|
||||
|
||||
|
||||
#endif //EQEMU_PROCESS_H
|
||||
@ -42,10 +42,32 @@
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
//Const char based
|
||||
#include "strings_legacy.cpp" // legacy c functions
|
||||
#include "strings_misc.cpp" // anything non "Strings" scoped
|
||||
|
||||
std::string Strings::Random(size_t length)
|
||||
{
|
||||
static auto &chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
thread_local static std::mt19937 rg{std::random_device{}()};
|
||||
|
||||
thread_local static std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);
|
||||
|
||||
std::string s;
|
||||
|
||||
s.reserve(length);
|
||||
|
||||
while (length--) {
|
||||
s += chrs[pick(rg)];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::string> Strings::Split(const std::string &str, const char delim)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
@ -64,7 +86,7 @@ std::vector<std::string> Strings::Split(const std::string &str, const char delim
|
||||
}
|
||||
|
||||
// this one takes delimiter length into consideration
|
||||
std::vector<std::string> Strings::Split(const std::string& s, const std::string& delimiter)
|
||||
std::vector<std::string> Strings::Split(const std::string &s, const std::string &delimiter)
|
||||
{
|
||||
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
|
||||
std::string token;
|
||||
@ -783,21 +805,6 @@ bool Strings::ToBool(const std::string& bool_string)
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns a random string of specified length
|
||||
std::string Strings::Random(size_t length)
|
||||
{
|
||||
auto randchar = []() -> char {
|
||||
const char charset[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[static_cast<size_t>(std::rand()) % max_index];
|
||||
};
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
}
|
||||
|
||||
// a wrapper for stoi which will return a fallback if the string
|
||||
// fails to cast to a number
|
||||
int Strings::ToInt(const std::string &s, int fallback)
|
||||
|
||||
@ -185,7 +185,6 @@ public:
|
||||
value = strtod(tmp_str.data(), nullptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const std::string StringFormat(const char *format, ...);
|
||||
|
||||
@ -133,6 +133,8 @@ SET(zone_sources
|
||||
quest_parser_collection.cpp
|
||||
raids.cpp
|
||||
raycast_mesh.cpp
|
||||
sidecar_api/sidecar_api.cpp
|
||||
sidecar_api/loot_simulator_controller.cpp
|
||||
shared_task_zone_messaging.cpp
|
||||
spawn2.cpp
|
||||
spawn2.h
|
||||
@ -253,6 +255,7 @@ SET(zone_headers
|
||||
quest_parser_collection.h
|
||||
raids.h
|
||||
raycast_mesh.h
|
||||
sidecar_api/sidecar_api.h
|
||||
shared_task_zone_messaging.h
|
||||
spawn2.cpp
|
||||
spawn2.h
|
||||
@ -273,8 +276,9 @@ SET(zone_headers
|
||||
zone_config.h
|
||||
zonedb.h
|
||||
zonedump.h
|
||||
zone_cli.h
|
||||
zone_reload.h
|
||||
)
|
||||
zone_cli.cpp)
|
||||
|
||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||
|
||||
|
||||
24
zone/cli/sidecar_serve_http.cpp
Normal file
24
zone/cli/sidecar_serve_http.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "../../common/http/httplib.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../sidecar_api/sidecar_api.h"
|
||||
#include "../../common/platform.h"
|
||||
|
||||
void ZoneCLI::SidecarServeHttp(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
if (cmd[{"-h", "--help"}]) {
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterExecutablePlatform(EQEmuExePlatform::ExePlatformZoneSidecar);
|
||||
|
||||
int port = 0;
|
||||
std::string key;
|
||||
if (!cmd("--port").str().empty()) {
|
||||
port = strtoll(cmd("--port").str().c_str(), nullptr, 10);
|
||||
}
|
||||
if (!cmd("--key").str().empty()) {
|
||||
key = cmd("--key").str();
|
||||
}
|
||||
|
||||
SidecarApi::BootWebserver(port, key);
|
||||
}
|
||||
196
zone/main.cpp
196
zone/main.cpp
@ -68,6 +68,7 @@
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
#include "../common/unix.h"
|
||||
|
||||
@ -85,6 +86,8 @@ extern volatile bool is_zone_loaded;
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/database/database_update.h"
|
||||
#include "zone_event_scheduler.h"
|
||||
#include "zone_cli.h"
|
||||
|
||||
EntityList entity_list;
|
||||
WorldServer worldserver;
|
||||
@ -112,17 +115,23 @@ const ZoneConfig *Config;
|
||||
double frame_time = 0.0;
|
||||
|
||||
void Shutdown();
|
||||
void UpdateWindowTitle(char* iNewTitle);
|
||||
void UpdateWindowTitle(char *iNewTitle);
|
||||
void CatchSignal(int sig_num);
|
||||
|
||||
extern void MapOpcodes();
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
RegisterExecutablePlatform(ExePlatformZone);
|
||||
LogSys.LoadLogSettingsDefaults();
|
||||
|
||||
set_exception_handler();
|
||||
|
||||
// silence logging if we ran a command
|
||||
if (ZoneCLI::RanConsoleCommand(argc, argv)) {
|
||||
LogSys.SilenceConsoleLogging();
|
||||
}
|
||||
|
||||
path.LoadPaths();
|
||||
|
||||
#ifdef USE_MAP_MMFS
|
||||
@ -154,77 +163,80 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
Config = ZoneConfig::get();
|
||||
|
||||
const char *zone_name;
|
||||
uint32 instance_id = 0;
|
||||
// static zone booting
|
||||
const char *zone_name;
|
||||
uint32 instance_id = 0;
|
||||
std::string z_name;
|
||||
if (argc == 4) {
|
||||
instance_id = Strings::ToInt(argv[3]);
|
||||
worldserver.SetLauncherName(argv[2]);
|
||||
auto zone_port = Strings::Split(argv[1], ':');
|
||||
if (!ZoneCLI::RanSidecarCommand(argc, argv)) {
|
||||
if (argc == 4) {
|
||||
instance_id = Strings::ToInt(argv[3]);
|
||||
worldserver.SetLauncherName(argv[2]);
|
||||
auto zone_port = Strings::Split(argv[1], ':');
|
||||
|
||||
if (!zone_port.empty()) {
|
||||
z_name = zone_port[0];
|
||||
if (!zone_port.empty()) {
|
||||
z_name = zone_port[0];
|
||||
}
|
||||
|
||||
if (zone_port.size() > 1) {
|
||||
std::string p_name = zone_port[1];
|
||||
Config->SetZonePort(Strings::ToInt(p_name));
|
||||
}
|
||||
|
||||
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 = Strings::Split(argv[1], ':');
|
||||
|
||||
if (zone_port.size() > 1) {
|
||||
std::string p_name = zone_port[1];
|
||||
Config->SetZonePort(Strings::ToInt(p_name));
|
||||
if (!zone_port.empty()) {
|
||||
z_name = zone_port[0];
|
||||
}
|
||||
|
||||
if (zone_port.size() > 1) {
|
||||
std::string p_name = zone_port[1];
|
||||
Config->SetZonePort(Strings::ToInt(p_name));
|
||||
}
|
||||
|
||||
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 = Strings::Split(argv[1], ':');
|
||||
|
||||
worldserver.SetLaunchedName(z_name.c_str());
|
||||
if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
|
||||
zone_name = ".";
|
||||
if (!zone_port.empty()) {
|
||||
z_name = zone_port[0];
|
||||
}
|
||||
|
||||
if (zone_port.size() > 1) {
|
||||
std::string p_name = zone_port[1];
|
||||
Config->SetZonePort(Strings::ToInt(p_name));
|
||||
}
|
||||
|
||||
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 = z_name.c_str();
|
||||
}
|
||||
}
|
||||
else if (argc == 3) {
|
||||
worldserver.SetLauncherName(argv[2]);
|
||||
auto zone_port = Strings::Split(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(Strings::ToInt(p_name));
|
||||
}
|
||||
|
||||
worldserver.SetLaunchedName(z_name.c_str());
|
||||
if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
|
||||
zone_name = ".";
|
||||
worldserver.SetLaunchedName(".");
|
||||
worldserver.SetLauncherName("NONE");
|
||||
}
|
||||
else {
|
||||
zone_name = z_name.c_str();
|
||||
}
|
||||
}
|
||||
else if (argc == 2) {
|
||||
worldserver.SetLauncherName("NONE");
|
||||
auto zone_port = Strings::Split(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(Strings::ToInt(p_name));
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
auto mutex = new Mutex;
|
||||
@ -235,7 +247,8 @@ int main(int argc, char** argv) {
|
||||
Config->DatabaseUsername.c_str(),
|
||||
Config->DatabasePassword.c_str(),
|
||||
Config->DatabaseDB.c_str(),
|
||||
Config->DatabasePort)) {
|
||||
Config->DatabasePort
|
||||
)) {
|
||||
LogError("Cannot continue without a database connection");
|
||||
return 1;
|
||||
}
|
||||
@ -243,7 +256,7 @@ int main(int argc, char** argv) {
|
||||
// Multi-tenancy: Content Database
|
||||
if (!Config->ContentDbHost.empty()) {
|
||||
if (!content_db.Connect(
|
||||
Config->ContentDbHost.c_str() ,
|
||||
Config->ContentDbHost.c_str(),
|
||||
Config->ContentDbUsername.c_str(),
|
||||
Config->ContentDbPassword.c_str(),
|
||||
Config->ContentDbName.c_str(),
|
||||
@ -280,7 +293,12 @@ int main(int argc, char** argv) {
|
||||
EQ::InitializeDynamicLookups();
|
||||
}
|
||||
|
||||
/* Register Log System and Settings */
|
||||
// command handler
|
||||
if (ZoneCLI::RanConsoleCommand(argc, argv) && !ZoneCLI::RanSidecarCommand(argc, argv)) {
|
||||
LogSys.EnableConsoleLogging();
|
||||
ZoneCLI::CommandHandler(argc, argv);
|
||||
}
|
||||
|
||||
LogSys.SetDatabase(&database)
|
||||
->SetLogPath(path.GetLogPath())
|
||||
->LoadLogDatabaseSettings()
|
||||
@ -455,6 +473,11 @@ int main(int argc, char** argv) {
|
||||
worldserver.Connect();
|
||||
worldserver.SetScheduler(&event_scheduler);
|
||||
|
||||
// sidecar command handler
|
||||
if (ZoneCLI::RanConsoleCommand(argc, argv) && ZoneCLI::RanSidecarCommand(argc, argv)) {
|
||||
ZoneCLI::CommandHandler(argc, argv);
|
||||
}
|
||||
|
||||
Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect
|
||||
#ifdef EQPROFILE
|
||||
#ifdef PROFILE_DUMP_TIME
|
||||
@ -486,13 +509,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
Timer quest_timers(100);
|
||||
UpdateWindowTitle(nullptr);
|
||||
std::shared_ptr<EQStreamInterface> eqss;
|
||||
EQStreamInterface *eqsi;
|
||||
std::unique_ptr<EQ::Net::EQStreamManager> eqsm;
|
||||
std::shared_ptr<EQStreamInterface> eqss;
|
||||
EQStreamInterface *eqsi;
|
||||
std::unique_ptr<EQ::Net::EQStreamManager> eqsm;
|
||||
std::chrono::time_point<std::chrono::system_clock> frame_prev = std::chrono::system_clock::now();
|
||||
std::unique_ptr<EQ::Net::WebsocketServer> ws_server;
|
||||
std::unique_ptr<EQ::Net::WebsocketServer> ws_server;
|
||||
|
||||
auto loop_fn = [&](EQ::Timer* t) {
|
||||
auto loop_fn = [&](EQ::Timer *t) {
|
||||
//Advance the timer to our current point in time
|
||||
Timer::SetCurrentTime();
|
||||
|
||||
@ -520,12 +543,12 @@ int main(int argc, char** argv) {
|
||||
LogInfo("Starting EQ Network server on port [{}]", Config->ZonePort);
|
||||
|
||||
EQStreamManagerInterfaceOptions opts(Config->ZonePort, false, RuleB(Network, CompressZoneStream));
|
||||
opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
|
||||
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);
|
||||
eqsm = std::make_unique<EQ::Net::EQStreamManager>(opts);
|
||||
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);
|
||||
eqsm = std::make_unique<EQ::Net::EQStreamManager>(opts);
|
||||
eqsf_open = true;
|
||||
|
||||
eqsm->OnNewConnection(
|
||||
@ -546,7 +569,7 @@ int main(int argc, char** argv) {
|
||||
//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;
|
||||
struct in_addr in;
|
||||
in.s_addr = eqsi->GetRemoteIP();
|
||||
LogInfo("New client from [{}]:[{}]", inet_ntoa(in), ntohs(eqsi->GetRemotePort()));
|
||||
auto client = new Client(eqsi);
|
||||
@ -558,7 +581,15 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
else {
|
||||
if (worldwasconnected && is_zone_loaded) {
|
||||
entity_list.ChannelMessageFromWorld(0, 0, ChatChannel_Broadcast, 0, 0, 100, "WARNING: World server connection lost");
|
||||
entity_list.ChannelMessageFromWorld(
|
||||
0,
|
||||
0,
|
||||
ChatChannel_Broadcast,
|
||||
0,
|
||||
0,
|
||||
100,
|
||||
"WARNING: World server connection lost"
|
||||
);
|
||||
worldwasconnected = false;
|
||||
}
|
||||
}
|
||||
@ -614,8 +645,9 @@ int main(int argc, char** argv) {
|
||||
|
||||
safe_delete(Config);
|
||||
|
||||
if (zone != 0)
|
||||
if (zone != 0) {
|
||||
Zone::Shutdown(true);
|
||||
}
|
||||
//Fix for Linux world server problem.
|
||||
safe_delete(task_manager);
|
||||
safe_delete(npc_scale_manager);
|
||||
@ -638,7 +670,8 @@ void Shutdown()
|
||||
EQ::EventLoop::Get().Shutdown();
|
||||
}
|
||||
|
||||
void CatchSignal(int sig_num) {
|
||||
void CatchSignal(int sig_num)
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
LogInfo("Recieved signal: [{}]", sig_num);
|
||||
#endif
|
||||
@ -646,7 +679,8 @@ void CatchSignal(int sig_num) {
|
||||
}
|
||||
|
||||
/* Update Window Title with relevant information */
|
||||
void UpdateWindowTitle(char* iNewTitle) {
|
||||
void UpdateWindowTitle(char *iNewTitle)
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
char tmp[500];
|
||||
if (iNewTitle) {
|
||||
|
||||
18
zone/sidecar_api/log_handler.cpp
Normal file
18
zone/sidecar_api/log_handler.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
void SidecarApi::RequestLogHandler(const httplib::Request &req, const httplib::Response &res)
|
||||
{
|
||||
if (!req.path.empty()) {
|
||||
std::vector<std::string> params;
|
||||
for (auto &p: req.params) {
|
||||
params.emplace_back(fmt::format("{}={}", p.first, p.second));
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"[API] Request [{}] [{}{}] via [{}:{}]",
|
||||
res.status,
|
||||
req.path,
|
||||
(!params.empty() ? "?" + Strings::Join(params, "&") : ""),
|
||||
req.remote_addr,
|
||||
req.remote_port
|
||||
);
|
||||
}
|
||||
}
|
||||
173
zone/sidecar_api/loot_simulator_controller.cpp
Normal file
173
zone/sidecar_api/loot_simulator_controller.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
#include "sidecar_api.h"
|
||||
#include "../../common/json/json.hpp"
|
||||
#include "../zone.h"
|
||||
|
||||
extern Zone* zone;
|
||||
|
||||
void SidecarApi::LootSimulatorController(const httplib::Request &req, httplib::Response &res)
|
||||
{
|
||||
int loottable_id = req.has_param("loottable_id") ? std::stoi(req.get_param_value("loottable_id")) : 4027;
|
||||
int npc_id = req.has_param("npc_id") ? std::stoi(req.get_param_value("npc_id")) : 32040; // lord nagafen
|
||||
auto iterations = 100;
|
||||
auto log_enabled = false;
|
||||
|
||||
LogSys.log_settings[Logs::Loot].log_to_console = 0;
|
||||
|
||||
nlohmann::json j;
|
||||
|
||||
auto npc_type = content_db.LoadNPCTypesData(npc_id);
|
||||
if (npc_type) {
|
||||
auto npc = new NPC(
|
||||
npc_type,
|
||||
nullptr,
|
||||
glm::vec4(0, 0, 0, 0),
|
||||
GravityBehavior::Water
|
||||
);
|
||||
BenchTimer benchmark;
|
||||
|
||||
// depop the previous one
|
||||
for (auto &n: entity_list.GetNPCList()) {
|
||||
if (n.second->GetNPCTypeID() == npc_id) {
|
||||
LogInfo("found npc id [{}]", npc_id);
|
||||
n.second->Depop(false);
|
||||
}
|
||||
}
|
||||
|
||||
entity_list.Process();
|
||||
entity_list.MobProcess();
|
||||
npc->SetRecordLootStats(true);
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
npc->AddLootTable(loottable_id);
|
||||
|
||||
for (auto &id: zone->GetGlobalLootTables(npc)) {
|
||||
npc->AddLootTable(id);
|
||||
}
|
||||
}
|
||||
|
||||
entity_list.AddNPC(npc);
|
||||
|
||||
j["data"]["loottable_id"] = loottable_id;
|
||||
j["data"]["npc_id"] = npc_id;
|
||||
j["data"]["npc_name"] = npc->GetCleanName();
|
||||
j["data"]["rolled_items_count"] = npc->GetRolledItems().size();
|
||||
j["data"]["iterations"] = iterations;
|
||||
|
||||
// npc level loot table
|
||||
auto loot_table = database.GetLootTable(loottable_id);
|
||||
if (!loot_table) {
|
||||
res.status = 400;
|
||||
j["error"] = fmt::format("Loot table not found [{}]", loottable_id);
|
||||
res.set_content(j.dump(), "application/json");
|
||||
return;
|
||||
}
|
||||
for (uint32 i = 0; i < loot_table->NumEntries; i++) {
|
||||
auto le = loot_table->Entries[i];
|
||||
|
||||
nlohmann::json jle;
|
||||
jle["lootdrop_id"] = le.lootdrop_id;
|
||||
jle["droplimit"] = le.droplimit;
|
||||
jle["mindrop"] = le.mindrop;
|
||||
jle["multiplier"] = le.multiplier;
|
||||
jle["probability"] = le.probability;
|
||||
|
||||
auto loot_drop = database.GetLootDrop(le.lootdrop_id);
|
||||
if (!loot_drop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 ei = 0; ei < loot_drop->NumEntries; ei++) {
|
||||
auto e = loot_drop->Entries[ei];
|
||||
int rolled_count = npc->GetRolledItemCount(e.item_id);
|
||||
const EQ::ItemData *item = database.GetItem(e.item_id);
|
||||
|
||||
|
||||
auto rolled_percentage = (float) ((float) ((float) rolled_count / (float) iterations) * 100);
|
||||
|
||||
nlohmann::json drop;
|
||||
drop["slot"] = ei;
|
||||
drop["item_id"] = e.item_id;
|
||||
drop["item_name"] = item->Name;
|
||||
drop["chance"] = fmt::format("{:.2f}", e.chance);
|
||||
drop["simulate_rolled_count"] = rolled_count;
|
||||
drop["simulate_rolled_percentage"] = fmt::format("{:.2f}", rolled_percentage);
|
||||
jle["drops"].push_back(drop);
|
||||
}
|
||||
|
||||
j["lootdrops"].push_back(jle);
|
||||
}
|
||||
|
||||
// global loot
|
||||
for (auto &id: zone->GetGlobalLootTables(npc)) {
|
||||
loot_table = database.GetLootTable(id);
|
||||
if (!loot_table) {
|
||||
LogInfo("Global Loot table not found [{}]", id);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < loot_table->NumEntries; i++) {
|
||||
auto le = loot_table->Entries[i];
|
||||
|
||||
LogInfo(
|
||||
"# Lootdrop ID [{}] drop_limit [{}] min_drop [{}] mult [{}] probability [{}]",
|
||||
le.lootdrop_id,
|
||||
le.droplimit,
|
||||
le.mindrop,
|
||||
le.multiplier,
|
||||
le.probability
|
||||
);
|
||||
|
||||
nlohmann::json jle;
|
||||
jle["lootdrop_id"] = le.lootdrop_id;
|
||||
jle["droplimit"] = le.droplimit;
|
||||
jle["mindrop"] = le.mindrop;
|
||||
jle["multiplier"] = le.multiplier;
|
||||
jle["probability"] = le.probability;
|
||||
|
||||
auto loot_drop = database.GetLootDrop(le.lootdrop_id);
|
||||
if (!loot_drop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 ei = 0; ei < loot_drop->NumEntries; ei++) {
|
||||
auto e = loot_drop->Entries[ei];
|
||||
int rolled_count = npc->GetRolledItemCount(e.item_id);
|
||||
const EQ::ItemData *item = database.GetItem(e.item_id);
|
||||
|
||||
auto rolled_percentage = (float) ((float) ((float) rolled_count / (float) iterations) *
|
||||
100);
|
||||
|
||||
|
||||
LogInfo(
|
||||
"-- [{}] item_id [{}] chance [{}] rolled_count [{}] ({:.2f}%) name [{}]",
|
||||
ei,
|
||||
e.item_id,
|
||||
e.chance,
|
||||
rolled_count,
|
||||
rolled_percentage,
|
||||
item->Name
|
||||
);
|
||||
|
||||
nlohmann::json drop;
|
||||
drop["slot"] = ei;
|
||||
drop["item_id"] = e.item_id;
|
||||
drop["item_name"] = item->Name;
|
||||
drop["chance"] = fmt::format("{:.2f}", e.chance);
|
||||
drop["simulate_rolled_count"] = rolled_count;
|
||||
drop["simulate_rolled_percentage"] = fmt::format("{:.2f}", rolled_percentage);
|
||||
jle["drops"].push_back(drop);
|
||||
|
||||
j["global"]["lootdrops"].push_back(jle);
|
||||
}
|
||||
}
|
||||
}
|
||||
j["data"]["time"] = benchmark.elapsed();
|
||||
res.status = 200;
|
||||
res.set_content(j.dump(), "application/json");
|
||||
}
|
||||
else {
|
||||
res.status = 400;
|
||||
j["error"] = fmt::format("Failed to spawn NPC ID [{}]", npc_id);
|
||||
res.set_content(j.dump(), "application/json");
|
||||
}
|
||||
}
|
||||
4
zone/sidecar_api/map_best_z_controller.cpp
Normal file
4
zone/sidecar_api/map_best_z_controller.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
void SidecarApi::MapBestZController(const httplib::Request &req, httplib::Response &res)
|
||||
{
|
||||
|
||||
}
|
||||
119
zone/sidecar_api/sidecar_api.cpp
Normal file
119
zone/sidecar_api/sidecar_api.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "sidecar_api.h"
|
||||
#include "../../common/http/httplib.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../zonedb.h"
|
||||
#include "../../shared_memory/loot.h"
|
||||
#include "../../common/process.h"
|
||||
#include "../common.h"
|
||||
#include "../zone.h"
|
||||
#include "../client.h"
|
||||
#include "../../common/json/json.hpp"
|
||||
#include <csignal>
|
||||
|
||||
void CatchSidecarSignal(int sig_num)
|
||||
{
|
||||
LogInfo("[SidecarAPI] Caught signal [{}]", sig_num);
|
||||
LogInfo("Forcefully exiting for now");
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
#include "log_handler.cpp"
|
||||
#include "test_controller.cpp"
|
||||
#include "map_best_z_controller.cpp"
|
||||
#include "../../common/file.h"
|
||||
|
||||
constexpr static int HTTP_RESPONSE_OK = 200;
|
||||
constexpr static int HTTP_RESPONSE_BAD_REQUEST = 400;
|
||||
constexpr static int HTTP_RESPONSE_UNAUTHORIZED = 401;
|
||||
|
||||
std::string authorization_key;
|
||||
|
||||
void SidecarApi::BootWebserver(int port, const std::string &key)
|
||||
{
|
||||
LogInfo("Booting zone sidecar API");
|
||||
|
||||
std::signal(SIGINT, CatchSidecarSignal);
|
||||
std::signal(SIGTERM, CatchSidecarSignal);
|
||||
std::signal(SIGKILL, CatchSidecarSignal);
|
||||
|
||||
if (!key.empty()) {
|
||||
authorization_key = key;
|
||||
LogInfo("Booting with authorization key [{}]", authorization_key);
|
||||
}
|
||||
|
||||
int web_api_port = port > 0 ? port : 9099;
|
||||
std::string hotfix_name = "zonesidecar_api_";
|
||||
|
||||
// bake shared memory if it doesn't exist
|
||||
// TODO: Windows
|
||||
if (!File::Exists("shared/zonesidecar_api_loot_drop")) {
|
||||
LogInfo("Creating shared memory for prefix [{}]", hotfix_name);
|
||||
|
||||
std::string output = Process::execute(
|
||||
fmt::format(
|
||||
"./bin/shared_memory -hotfix={} loot items",
|
||||
hotfix_name
|
||||
)
|
||||
);
|
||||
std::cout << output << "\n";
|
||||
}
|
||||
|
||||
LogInfo("Loading loot tables");
|
||||
if (!database.LoadLoot(hotfix_name)) {
|
||||
LogError("Loading loot failed!");
|
||||
}
|
||||
|
||||
// bootup a fake zone
|
||||
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||
zone->StopShutdownTimer();
|
||||
|
||||
httplib::Server api;
|
||||
|
||||
api.set_logger(SidecarApi::RequestLogHandler);
|
||||
api.set_pre_routing_handler(
|
||||
[](const auto &req, auto &res) {
|
||||
for (const auto &header: req.headers) {
|
||||
auto header_key = header.first;
|
||||
auto header_value = header.second;
|
||||
|
||||
LogHTTPDetail("[API] header_key [{}] header_value [{}]", header_key, header_value);
|
||||
|
||||
if (header_key == "Authorization") {
|
||||
std::string auth_key = header_value;
|
||||
Strings::FindReplace(auth_key, "Bearer", "");
|
||||
Strings::Trim(auth_key);
|
||||
|
||||
LogHTTPDetail(
|
||||
"Request Authorization key is [{}] set key is [{}] match [{}]",
|
||||
auth_key,
|
||||
authorization_key,
|
||||
auth_key == authorization_key ? "true" : "false"
|
||||
);
|
||||
|
||||
// authorization key matches, pass the request on to the route handler
|
||||
if (!authorization_key.empty() && auth_key != authorization_key) {
|
||||
LogHTTPDetail("[Sidecar] Returning as unhandled, authorization passed");
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!authorization_key.empty()) {
|
||||
nlohmann::json j;
|
||||
j["error"] = "Authorization key not valid!";
|
||||
res.set_content(j.dump(), "application/json");
|
||||
res.status = HTTP_RESPONSE_UNAUTHORIZED;
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
}
|
||||
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
}
|
||||
);
|
||||
api.Get("/api/v1/test-controller", SidecarApi::TestController);
|
||||
api.Get("/api/v1/loot-simulate", SidecarApi::LootSimulatorController);
|
||||
|
||||
LogInfo("Webserver API now listening on port [{0}]", web_api_port);
|
||||
|
||||
// this is not supposed to bind to the outside world
|
||||
api.listen("localhost", web_api_port);
|
||||
}
|
||||
17
zone/sidecar_api/sidecar_api.h
Normal file
17
zone/sidecar_api/sidecar_api.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef EQEMU_SIDECAR_API_H
|
||||
#define EQEMU_SIDECAR_API_H
|
||||
|
||||
#include "../../common/http/httplib.h"
|
||||
|
||||
class SidecarApi {
|
||||
public:
|
||||
static void BootWebserver(int req = 0, const std::string& key = "");
|
||||
static void AuthMiddleware(const httplib::Request &req, const httplib::Response &res);
|
||||
static void RequestLogHandler(const httplib::Request &req, const httplib::Response &res);
|
||||
static void TestController(const httplib::Request &req, httplib::Response &res);
|
||||
static void LootSimulatorController(const httplib::Request &req, httplib::Response &res);
|
||||
static void MapBestZController(const httplib::Request &req, httplib::Response &res);
|
||||
};
|
||||
|
||||
|
||||
#endif //EQEMU_SIDECAR_API_H
|
||||
10
zone/sidecar_api/test_controller.cpp
Normal file
10
zone/sidecar_api/test_controller.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include "sidecar_api.h"
|
||||
|
||||
void SidecarApi::TestController(const httplib::Request &req, httplib::Response &res)
|
||||
{
|
||||
nlohmann::json j;
|
||||
|
||||
j["data"]["test"] = "test";
|
||||
|
||||
res.set_content(j.dump(), "application/json");
|
||||
}
|
||||
@ -1821,6 +1821,11 @@ void Zone::ResetShutdownTimer() {
|
||||
autoshutdown_timer.Start(autoshutdown_timer.GetDuration(), true);
|
||||
}
|
||||
|
||||
void Zone::StopShutdownTimer() {
|
||||
LogInfo("Stopping zone shutdown timer");
|
||||
autoshutdown_timer.Disable();
|
||||
}
|
||||
|
||||
bool Zone::Depop(bool StartSpawnTimer) {
|
||||
std::map<uint32,NPCType *>::iterator itr;
|
||||
entity_list.Depop(StartSpawnTimer);
|
||||
|
||||
@ -302,6 +302,7 @@ public:
|
||||
void SpawnConditionChanged(const SpawnCondition &c, int16 old_value);
|
||||
void StartShutdownTimer(uint32 set_time = (RuleI(Zone, AutoShutdownDelay)));
|
||||
void ResetShutdownTimer();
|
||||
void StopShutdownTimer();
|
||||
void UpdateQGlobal(uint32 qid, QGlobal newGlobal);
|
||||
void weatherSend(Client *client = nullptr);
|
||||
void ClearSpawnTimers();
|
||||
|
||||
32
zone/zone_cli.cpp
Normal file
32
zone/zone_cli.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "zone_cli.h"
|
||||
#include "../common/cli/eqemu_command_handler.h"
|
||||
#include <string.h>
|
||||
|
||||
bool ZoneCLI::RanConsoleCommand(int argc, char **argv)
|
||||
{
|
||||
return argc > 1 && (strstr(argv[1], ":") != nullptr || strstr(argv[1], "--") != nullptr);
|
||||
}
|
||||
|
||||
bool ZoneCLI::RanSidecarCommand(int argc, char **argv)
|
||||
{
|
||||
return argc > 1 && (strstr(argv[1], "sidecar:") != nullptr);
|
||||
}
|
||||
|
||||
void ZoneCLI::CommandHandler(int argc, char **argv)
|
||||
{
|
||||
if (argc == 1) { return; }
|
||||
|
||||
argh::parser cmd;
|
||||
cmd.parse(argc, argv, argh::parser::PREFER_PARAM_FOR_UNREG_OPTION);
|
||||
EQEmuCommand::DisplayDebug(cmd);
|
||||
|
||||
// Declare command mapping
|
||||
auto function_map = EQEmuCommand::function_map;
|
||||
|
||||
// Register commands
|
||||
function_map["sidecar:serve-http"] = &ZoneCLI::SidecarServeHttp;
|
||||
|
||||
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
||||
}
|
||||
|
||||
#include "cli/sidecar_serve_http.cpp"
|
||||
15
zone/zone_cli.h
Normal file
15
zone/zone_cli.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef EQEMU_ZONE_CLI_H
|
||||
#define EQEMU_ZONE_CLI_H
|
||||
|
||||
#include "../common/cli/argh.h"
|
||||
|
||||
class ZoneCLI {
|
||||
public:
|
||||
static void CommandHandler(int argc, char **argv);
|
||||
static void SidecarServeHttp(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static bool RanConsoleCommand(int argc, char **argv);
|
||||
static bool RanSidecarCommand(int argc, char **argv);
|
||||
};
|
||||
|
||||
|
||||
#endif //EQEMU_ZONE_CLI_H
|
||||
Loading…
x
Reference in New Issue
Block a user