Compare commits

..

28 Commits

Author SHA1 Message Date
Akkadius 2237c3a056 [Release] 22.31.1 2023-10-31 08:08:29 -05:00
Akkadius 4af191c593 [Hotfix] Fix issue with blocked spells not loading properly 2023-10-31 08:06:51 -05:00
Chris Miles 0a3972deb9 [Release] 22.31.0 (#3662) 2023-10-29 18:50:46 -05:00
Chris Miles 9d2f258390 [Database] Add id to variables table (#3658) 2023-10-29 18:45:30 -05:00
Chris Miles 0b452f4ec1 [Crash] Fix spell in AESpell related to beacons (#3659) 2023-10-29 18:45:24 -05:00
Chris Miles fef629e1df [Crash] Fix crash when client pointer does not exist during #hotfix (#3661) 2023-10-29 18:45:18 -05:00
Chris Miles a5a51fbe44 [Linux] Add symbols to release builds (#3660)
* [Linux] Add symbols to release builds

* Update linux-build.sh
2023-10-29 18:45:11 -05:00
Paul Coene 47db92cdb6 [Trading] Fix part 3 of Issue 932. (#3654) 2023-10-29 09:50:04 -04:00
Akkadius 690301e80d [Release] 22.30.2 - Hotfix perl loading 2023-10-26 16:23:55 -05:00
Akkadius 1887e48b76 Revert "[Perl] Reload perl quests on zone bootup (#3648)"
This reverts commit 0bbfcf7adc.
2023-10-26 16:21:06 -05:00
JJ af2691eb12 [Release] 22.30.1 (#3656)
* Update package.json

* Update CHANGELOG.md

* Update version.h
2023-10-24 20:27:54 -04:00
Alex King 2df4289588 [Bug Fix] Fix empty InsertMany in bot starting items. (#3653)
Not checking if the vector is empty before it inserts still inserts when there’s nothing.
2023-10-24 18:15:42 -04:00
Akkadius 49d4d0acc3 [Release] 22.29.0 2023-10-23 22:41:58 -05:00
Paul Coene 5a663910a5 [Pets] Disallow effect of alliance line when cast on pets. (#3650)
* [Pets] Disallow effect of alliance line when cast on pets.

* Update spell_effects.cpp

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2023-10-23 22:40:24 -05:00
Chris Miles b027edd21e [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
2023-10-23 22:39:37 -05:00
hg 0bbfcf7adc [Perl] Reload perl quests on zone bootup (#3648)
Perl wasn't implementing quest interface's Init which is called by
Zone::Bootup. This should fix zone's that were in standby not using the
latest version of perl plugin scripts.
2023-10-23 22:39:19 -05:00
Chris Miles 7962a0bd38 [Perl] Implement eqemu-perl for Linux (#3652)
* [Perl] Implement eqemu-perl for Linux

* Update embperl.cpp
2023-10-23 22:38:46 -05:00
JJ 4d9b51df0a [Commands] Move #suspend from content database (#3651) 2023-10-23 20:01:49 -04:00
Alex King 508ecec6ea [Bug Fix] Fix Bot Starting Items SQL (#3649) 2023-10-23 17:19:30 -04:00
JJ f0c6fa2a26 [Release] 22.29.1 (#3647)
* Update version.h

* Update Changelog

* Update CHANGELOG.md

* Update package.json
2023-10-21 18:36:38 -05:00
JJ ad6dbb7beb [DB] Fix manifest for blocked spells (#3646) 2023-10-21 18:01:57 -04:00
JJ 6ddbb41617 [Bug Fix] Verifying mail keys when none exist (#3645)
No need to verify mail key when none exist.
Seen in http://spire.akkadius.com/dev/release/22.28.0?id=12069
2023-10-21 13:33:24 -04:00
JJ 8a558f6a29 [Bug Fix] Hotfix command without hotfix name (#3644)
If no hotfix name is provided, the hotfix command won't need the empty string.
2023-10-21 13:33:16 -04:00
Alex King 0585be0360 [Bug Fix] Fix issue with subcommand settings not working (#3643)
* [Bug Fix] Fix issue with subcommand settings not working

# Notes
- We were checking for `arguments >= 2` when we should just be checking for if there are any arguments and comparing `sep.arg[0]` (the command) and `sep.arg[1]` (the subcommand) to our `command_subsettings` to see if it exists and if we pass the requirements.
- This fix will let operators properly set a subcommand to a lower or higher status level than the parent command.

* Remove debug message.
2023-10-20 21:33:05 -04:00
Akkadius 6927baef7f [Release] 22.29.0 2023-10-20 17:47:35 -05:00
Alex King 52d64781b5 [Feature] Add Expansion and Content Flag support to Blocked Spells (#3638)
* [Feature] Add Expansion and Content Flag support to Blocked Spells

# Notes
- Allows operators to filter blocked spells behind expansions or content flags.
- Requested in https://github.com/EQEmu/Server/issues/3582

* [Tradeskills] Add learned_by_item_id field (#3637)

* [Feature] Add Expansion and Content Flag support to Blocked Spells

- Allows operators to filter blocked spells behind expansions or content flags.
- Requested in https://github.com/EQEmu/Server/issues/3582

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2023-10-20 17:45:58 -05:00
Aeadoin 0667fe435f [Bug Fix] Fix crash when checking Bot Group/Raid membership (#3641)
* [Bug Fix] Fix crash when checking Bot Group/Raid membership

* Update bot.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-10-20 17:45:41 -05:00
Chris Miles 9959070f24 [Perl] Static linker fix on Linux (#3642)
* Update CMakeLists.txt

* Update linux-build.sh

* test

* Test

* test

* brute force

* !?!

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Remove testing

* Update linux-build.sh
2023-10-20 17:43:04 -05:00
42 changed files with 25556 additions and 201 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ volumes:
steps: steps:
- name: Build Linux X64 - name: Build Linux X64
image: akkadius/eqemu-server:v11 image: akkadius/eqemu-server:v13
environment: environment:
GITHUB_TOKEN: GITHUB_TOKEN:
from_secret: GH_RELEASE_GITHUB_API_TOKEN from_secret: GH_RELEASE_GITHUB_API_TOKEN
+94
View File
@@ -1,3 +1,97 @@
## [22.31.1] - 10/31/2023
### Fixes
* Hotfix issue with blocked spells not loading properly @Akkadius 2023-10-31
## [22.31.0] - 10/29/2023
### Crash
* Fix crash when client pointer does not exist during #hotfix ([#3661](https://github.com/EQEmu/Server/pull/3661)) @Akkadius 2023-10-29
* Fix spell in AESpell related to beacons ([#3659](https://github.com/EQEmu/Server/pull/3659)) @Akkadius 2023-10-29
### Database
* Add id to variables table ([#3658](https://github.com/EQEmu/Server/pull/3658)) @Akkadius 2023-10-29
### Linux
* Add symbols to release builds ([#3660](https://github.com/EQEmu/Server/pull/3660)) @Akkadius 2023-10-29
### Perl
* Revert " Reload perl quests on zone bootup " ([#3648](https://github.com/EQEmu/Server/pull/3648)) @Akkadius 2023-10-26
### Trading
* Fix part 3 of Issue 932. ([#3654](https://github.com/EQEmu/Server/pull/3654)) @noudess 2023-10-29
## [22.30.2] - 10/26/2023
### Fixes
Revert Perl regression in #3648 causing scripts to not reliably initialize on zone bootup. @Akkadius 2023-10-26
## [22.30.1] - 10/24/2023
### Fixes
* Fix empty InsertMany in bot starting items. ([#3653](https://github.com/EQEmu/Server/pull/3653)) @Kinglykrab 2023-10-24
## [22.30.0] - 10/23/2023
### API
* Implement Zone Sidecar ([#3635](https://github.com/EQEmu/Server/pull/3635)) @Akkadius 2023-10-24
### Commands
* Move #suspend from content database ([#3651](https://github.com/EQEmu/Server/pull/3651)) @joligario 2023-10-24
### Fixes
* Fix Bot Starting Items SQL ([#3649](https://github.com/EQEmu/Server/pull/3649)) @Kinglykrab 2023-10-23
### Perl
* Implement eqemu-perl for Linux ([#3652](https://github.com/EQEmu/Server/pull/3652)) @Akkadius 2023-10-24
* Reload perl quests on zone bootup ([#3648](https://github.com/EQEmu/Server/pull/3648)) @hgtw 2023-10-24
### Pets
* Disallow effect of alliance line when cast on pets. ([#3650](https://github.com/EQEmu/Server/pull/3650)) @noudess 2023-10-24
## [22.29.1] - 10/21/2023
### DB
* Fix manifest for blocked spells ([#3646](https://github.com/EQEmu/Server/pull/3646)) @joligario 2023-10-21
### Fixes
* Fix issue with subcommand settings not working ([#3643](https://github.com/EQEmu/Server/pull/3643)) @Kinglykrab 2023-10-21
* Hotfix command without hotfix name ([#3644](https://github.com/EQEmu/Server/pull/3644)) @joligario 2023-10-21
* Verifying mail keys when none exist ([#3645](https://github.com/EQEmu/Server/pull/3645)) @joligario 2023-10-21
## [22.29.0] - 10/20/2023
### Feature
* Add Expansion and Content Flag support to Blocked Spells ([#3638](https://github.com/EQEmu/Server/pull/3638)) @Kinglykrab 2023-10-20
### Fixes
* Fix crash when checking Bot Group/Raid membership ([#3641](https://github.com/EQEmu/Server/pull/3641)) @Aeadoin 2023-10-20
### Perl
* Static linker fix on Linux ([#3642](https://github.com/EQEmu/Server/pull/3642)) @Akkadius 2023-10-20
### Rules
* Add rule to configure max number of procs per round Combat:MaxProcs ([#3640](https://github.com/EQEmu/Server/pull/3640)) @Akkadius 2023-10-20
## [22.28.1] - 10/20/2023 ## [22.28.1] - 10/20/2023
### Build ### Build
+15
View File
@@ -23,6 +23,11 @@ IF (EQEMU_BUILD_STATIC)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a") SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a")
MESSAGE(STATUS "Building with static linking") MESSAGE(STATUS "Building with static linking")
SET(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") SET(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
IF (UNIX)
SET(PERL_LIBRARY "/opt/eqemu-perl/lib/5.32.1/x86_64-linux-thread-multi/CORE/libperl.so")
SET(PERL_INCLUDE_PATH "/opt/eqemu-perl/lib/5.32.1/x86_64-linux-thread-multi/CORE/")
SET(PERL_EXECUTABLE "/opt/eqemu-perl/bin/perl")
ENDIF ()
ENDIF (EQEMU_BUILD_STATIC) ENDIF (EQEMU_BUILD_STATIC)
IF(MSVC) IF(MSVC)
@@ -133,6 +138,13 @@ ELSE()
MESSAGE(STATUS "* mbedTLS: MISSING *") MESSAGE(STATUS "* mbedTLS: MISSING *")
ENDIF() ENDIF()
MESSAGE(STATUS "PERL_INCLUDE_PATH: ${PERL_INCLUDE_PATH}")
MESSAGE(STATUS "PERL_LIBRARY: ${PERL_LIBRARY}")
MESSAGE(STATUS "PERL_INCLUDE_DIR: ${PERL_INCLUDE_DIR}")
MESSAGE(STATUS "PERL_INCLUDE_DIRS: ${PERL_INCLUDE_DIRS}")
MESSAGE(STATUS "PERL_LIBRARIES: ${PERL_LIBRARIES}")
MESSAGE(STATUS "PERL_VERSION: ${PERL_VERSION}")
MESSAGE(STATUS "**************************************************") MESSAGE(STATUS "**************************************************")
#options #options
@@ -389,6 +401,9 @@ IF(PERL_LIBRARY_ENABLED)
ADD_DEFINITIONS(-DEMBPERL) ADD_DEFINITIONS(-DEMBPERL)
ADD_DEFINITIONS(-DEMBPERL_PLUGIN) ADD_DEFINITIONS(-DEMBPERL_PLUGIN)
ADD_DEFINITIONS(-DPERLBIND_NO_STRICT_SCALAR_TYPES) ADD_DEFINITIONS(-DPERLBIND_NO_STRICT_SCALAR_TYPES)
IF (UNIX AND EQEMU_BUILD_STATIC)
SET(SERVER_LIBS ${SERVER_LIBS} libcrypt.a)
ENDIF ()
ENDIF() ENDIF()
ENDIF() ENDIF()
+3
View File
@@ -70,6 +70,7 @@ SET(common_sources
perl_eqdb.cpp perl_eqdb.cpp
perl_eqdb_res.cpp perl_eqdb_res.cpp
process/process.cpp process/process.cpp
process.cpp
proc_launcher.cpp proc_launcher.cpp
profanity_manager.cpp profanity_manager.cpp
ptimer.cpp ptimer.cpp
@@ -90,6 +91,7 @@ SET(common_sources
timer.cpp timer.cpp
unix.cpp unix.cpp
platform.cpp platform.cpp
json/json.hpp
json/jsoncpp.cpp json/jsoncpp.cpp
zone_store.cpp zone_store.cpp
net/console_server.cpp net/console_server.cpp
@@ -583,6 +585,7 @@ SET(common_headers
path_manager.cpp path_manager.cpp
platform.h platform.h
process/process.h process/process.h
process.h
proc_launcher.h proc_launcher.h
profanity_manager.h profanity_manager.h
profiler.h profiler.h
+13 -20
View File
@@ -124,14 +124,6 @@ namespace EQEmuCommand {
) )
{ {
std::string description; 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"}]) { if (cmd[{"-h", "--help"}]) {
std::cout << std::endl; std::cout << std::endl;
std::cout << std::cout <<
@@ -142,9 +134,7 @@ namespace EQEmuCommand {
<< std::endl << std::endl
<< std::endl; << std::endl;
/** // Get max command length for padding length
* Get max command length for padding length
*/
int max_command_length = 0; int max_command_length = 0;
for (auto &it: in_function_map) { for (auto &it: in_function_map) {
@@ -155,18 +145,14 @@ namespace EQEmuCommand {
} }
} }
/** // Display command menu
* Display command menu
*/
std::string command_section; std::string command_section;
for (auto &it: in_function_map) { for (auto &it: in_function_map) {
description.clear(); description.clear();
(it.second)(argc, argv, cmd, description); (it.second)(argc, argv, cmd, description);
/** // Print section header
* Print section header
*/
std::string command_prefix = it.first.substr(0, it.first.find(":")); std::string command_prefix = it.first.substr(0, it.first.find(":"));
if (command_prefix.find("test") != std::string::npos) { if (command_prefix.find("test") != std::string::npos) {
@@ -178,9 +164,7 @@ namespace EQEmuCommand {
std::cout << termcolor::reset << command_prefix << std::endl; std::cout << termcolor::reset << command_prefix << std::endl;
} }
/** // Print commands
* Print commands
*/
std::stringstream command; std::stringstream command;
command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset; command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset;
printf(" %-*s %s\n", max_command_length, command.str().c_str(), description.c_str()); printf(" %-*s %s\n", max_command_length, command.str().c_str(), description.c_str());
@@ -191,6 +175,15 @@ namespace EQEmuCommand {
std::exit(0); 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) { if (ran_command) {
std::exit(0); std::exit(0);
} }
@@ -4967,6 +4967,34 @@ ALTER TABLE `items`
.sql = R"( .sql = R"(
ALTER TABLE `tradeskill_recipe` ALTER TABLE `tradeskill_recipe`
ADD COLUMN `learned_by_item_id` int(11) NOT NULL DEFAULT 0 AFTER `must_learn`; ADD COLUMN `learned_by_item_id` int(11) NOT NULL DEFAULT 0 AFTER `must_learn`;
)"
},
ManifestEntry{
.version = 9239,
.description = "2023_10_18_blocked_spells_expansions_content_flags.sql",
.check = "SHOW COLUMNS FROM `blocked_spells` LIKE 'min_expansion'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `blocked_spells`
ADD COLUMN `min_expansion` tinyint(4) NOT NULL DEFAULT -1 AFTER `description`,
ADD COLUMN `max_expansion` tinyint(4) NOT NULL DEFAULT -1 AFTER `min_expansion`,
ADD COLUMN `content_flags` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `max_expansion`,
ADD COLUMN `content_flags_disabled` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `content_flags`;
)"
},
ManifestEntry{
.version = 9240,
.description = "2023_10_29_variables_id.sql",
.check = "SHOW COLUMNS FROM `variables` LIKE 'id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `variables`
ADD COLUMN `id` int(11) NOT NULL AUTO_INCREMENT FIRST,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`) USING BTREE,
ADD UNIQUE INDEX(`varname`);
)" )"
}, },
@@ -65,8 +65,8 @@ SET FOREIGN_KEY_CHECKS = 1;
.version = 9040, .version = 9040,
.description = "2023_11_16_bot_starting_items.sql", .description = "2023_11_16_bot_starting_items.sql",
.check = "SHOW TABLES LIKE 'bot_starting_items'", .check = "SHOW TABLES LIKE 'bot_starting_items'",
.condition = "", .condition = "empty",
.match = "empty", .match = "",
.sql = R"( .sql = R"(
CREATE TABLE `bot_starting_items` ( CREATE TABLE `bot_starting_items` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+24640
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -72,6 +72,8 @@ std::string GetPlatformName()
return "HC"; return "HC";
case EQEmuExePlatform::ExePlatformTests: case EQEmuExePlatform::ExePlatformTests:
return "Tests"; return "Tests";
case EQEmuExePlatform::ExePlatformZoneSidecar:
return "ZoneSidecar";
default: default:
return ""; return "";
} }
+2 -1
View File
@@ -37,7 +37,8 @@ enum EQEmuExePlatform
ExePlatformClientImport, ExePlatformClientImport,
ExePlatformClientExport, ExePlatformClientExport,
ExePlatformHC, ExePlatformHC,
ExePlatformTests ExePlatformTests,
ExePlatformZoneSidecar
}; };
void RegisterExecutablePlatform(EQEmuExePlatform p); void RegisterExecutablePlatform(EQEmuExePlatform p);
+44
View 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
View 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
+22 -15
View File
@@ -42,10 +42,32 @@
#include <stdio.h> #include <stdio.h>
#include <iostream> #include <iostream>
#include <random>
#include <string>
//Const char based //Const char based
#include "strings_legacy.cpp" // legacy c functions #include "strings_legacy.cpp" // legacy c functions
#include "strings_misc.cpp" // anything non "Strings" scoped #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> Strings::Split(const std::string &str, const char delim)
{ {
std::vector<std::string> ret; std::vector<std::string> ret;
@@ -783,21 +805,6 @@ bool Strings::ToBool(const std::string& bool_string)
return false; 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 // a wrapper for stoi which will return a fallback if the string
// fails to cast to a number // fails to cast to a number
int Strings::ToInt(const std::string &s, int fallback) int Strings::ToInt(const std::string &s, int fallback)
-1
View File
@@ -185,7 +185,6 @@ public:
value = strtod(tmp_str.data(), nullptr); value = strtod(tmp_str.data(), nullptr);
return res; return res;
} }
}; };
const std::string StringFormat(const char *format, ...); const std::string StringFormat(const char *format, ...);
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables // Build variables
// these get injected during the build pipeline // these get injected during the build pipeline
#define CURRENT_VERSION "22.28.0-dev" // always append -dev to the current version for custom-builds #define CURRENT_VERSION "22.31.1-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0" #define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9238 #define CURRENT_BINARY_DATABASE_VERSION 9240
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9040 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9040
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "eqemu-server", "name": "eqemu-server",
"version": "22.28.1", "version": "22.31.1",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EQEmu/Server.git" "url": "https://github.com/EQEmu/Server.git"
+5
View File
@@ -156,6 +156,11 @@ bool UCSDatabase::VerifyMailKey(const std::string& characterName, int IPAddress,
return false; return false;
} }
if (results.RowCount() == 0) {
LogInfo("No mailkeys found for [{}].", characterName.c_str());
return false;
}
auto row = results.begin(); auto row = results.begin();
// The key is the client's IP address (expressed as 8 hex digits) and an 8 hex digit random string generated // The key is the client's IP address (expressed as 8 hex digits) and an 8 hex digit random string generated
+11 -1
View File
@@ -9,7 +9,17 @@ git submodule init && git submodule update
perl utils/scripts/build/tag-version.pl perl utils/scripts/build/tag-version.pl
mkdir -p build && cd build && cmake -DEQEMU_BUILD_TESTS=ON -DEQEMU_BUILD_STATIC=ON -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -DEQEMU_BUILD_PERL=ON -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-Os" -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -G 'Unix Makefiles' .. && make -j$((`nproc`-4)) mkdir -p build && cd build && \
cmake -DEQEMU_BUILD_TESTS=ON \
-DEQEMU_BUILD_STATIC=ON \
-DEQEMU_BUILD_LOGIN=ON \
-DEQEMU_BUILD_LUA=ON \
-DEQEMU_BUILD_PERL=ON \
-DCMAKE_CXX_FLAGS:STRING="-O1 -g" \
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-O1 -g" \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-G 'Unix Makefiles' \
.. && make -j$((`nproc`-4))
curl https://raw.githubusercontent.com/Akkadius/eqemu-install-v2/master/eqemu_config.json --output eqemu_config.json curl https://raw.githubusercontent.com/Akkadius/eqemu-install-v2/master/eqemu_config.json --output eqemu_config.json
./bin/tests ./bin/tests
+5 -1
View File
@@ -133,6 +133,8 @@ SET(zone_sources
quest_parser_collection.cpp quest_parser_collection.cpp
raids.cpp raids.cpp
raycast_mesh.cpp raycast_mesh.cpp
sidecar_api/sidecar_api.cpp
sidecar_api/loot_simulator_controller.cpp
shared_task_zone_messaging.cpp shared_task_zone_messaging.cpp
spawn2.cpp spawn2.cpp
spawn2.h spawn2.h
@@ -253,6 +255,7 @@ SET(zone_headers
quest_parser_collection.h quest_parser_collection.h
raids.h raids.h
raycast_mesh.h raycast_mesh.h
sidecar_api/sidecar_api.h
shared_task_zone_messaging.h shared_task_zone_messaging.h
spawn2.cpp spawn2.cpp
spawn2.h spawn2.h
@@ -273,8 +276,9 @@ SET(zone_headers
zone_config.h zone_config.h
zonedb.h zonedb.h
zonedump.h zonedump.h
zone_cli.h
zone_reload.h zone_reload.h
) zone_cli.cpp)
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
+6 -2
View File
@@ -3336,7 +3336,8 @@ bool Bot::Spawn(Client* botCharacterOwner) {
if (auto raid = entity_list.GetRaidByBotName(GetName())) { if (auto raid = entity_list.GetRaidByBotName(GetName())) {
// Safety Check to confirm we have a valid raid // Safety Check to confirm we have a valid raid
if (!raid->IsRaidMember(GetBotOwner()->GetName())) { auto owner = GetBotOwner();
if (owner && !raid->IsRaidMember(owner->GetCleanName())) {
Bot::RemoveBotFromRaid(this); Bot::RemoveBotFromRaid(this);
} else { } else {
SetRaidGrouped(true); SetRaidGrouped(true);
@@ -3346,7 +3347,8 @@ bool Bot::Spawn(Client* botCharacterOwner) {
} }
else if (auto group = entity_list.GetGroupByMobName(GetName())) { else if (auto group = entity_list.GetGroupByMobName(GetName())) {
// Safety Check to confirm we have a valid group // Safety Check to confirm we have a valid group
if (!group->IsGroupMember(GetBotOwner()->GetName())) { auto owner = GetBotOwner();
if (owner && !group->IsGroupMember(owner->GetCleanName())) {
Bot::RemoveBotFromGroup(this, group); Bot::RemoveBotFromGroup(this, group);
} else { } else {
SetGrouped(true); SetGrouped(true);
@@ -8771,7 +8773,9 @@ void Bot::AddBotStartingItems(uint16 race_id, uint8 class_id)
} }
} }
if (!v.empty()) {
BotInventoriesRepository::InsertMany(content_db, v); BotInventoriesRepository::InsertMany(content_db, v);
} }
}
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 }; uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
+24
View 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);
}
+13 -13
View File
@@ -437,13 +437,13 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
{ {
Seperator sep(message.c_str(), ' ', 10, 100, true); // "three word argument" should be considered 1 arg Seperator sep(message.c_str(), ' ', 10, 100, true); // "three word argument" should be considered 1 arg
std::string cstr(sep.arg[0] + 1); std::string command(sep.arg[0] + 1);
if (commandlist.count(cstr) != 1) { if (commandlist.count(command) != 1) {
return -2; return -2;
} }
auto cur = commandlist[cstr]; const CommandRecord* current_command = commandlist[command];
bool is_subcommand = false; bool is_subcommand = false;
bool can_use_subcommand = false; bool can_use_subcommand = false;
@@ -451,11 +451,11 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
const auto arguments = sep.argnum; const auto arguments = sep.argnum;
if (arguments >= 2) { if (arguments) {
const std::string& sub_command = sep.arg[1]; const std::string& sub_command = sep.arg[1];
for (const auto &e : command_subsettings) { for (const auto &e : command_subsettings) {
if (e.sub_command == sub_command) { if (e.parent_command == command && e.sub_command == sub_command) {
can_use_subcommand = c->Admin() >= static_cast<int16>(e.access_level); can_use_subcommand = c->Admin() >= static_cast<int16>(e.access_level);
is_subcommand = true; is_subcommand = true;
found_subcommand_setting = true; found_subcommand_setting = true;
@@ -465,7 +465,7 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
if (!found_subcommand_setting) { if (!found_subcommand_setting) {
for (const auto &e: command_subsettings) { for (const auto &e: command_subsettings) {
if (e.sub_command == sub_command) { if (e.parent_command == command && e.sub_command == sub_command) {
can_use_subcommand = c->Admin() >= static_cast<int16>(e.access_level); can_use_subcommand = c->Admin() >= static_cast<int16>(e.access_level);
is_subcommand = true; is_subcommand = true;
break; break;
@@ -475,7 +475,7 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
} }
if (!ignore_status) { if (!ignore_status) {
if (!is_subcommand && c->Admin() < cur->admin) { if (!is_subcommand && c->Admin() < current_command->admin) {
c->Message(Chat::White, "Your status is not high enough to use this command."); c->Message(Chat::White, "Your status is not high enough to use this command.");
return -1; return -1;
} }
@@ -497,7 +497,7 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc);
} }
if (cur->admin >= COMMANDS_LOGGING_MIN_STATUS) { if (current_command->admin >= COMMANDS_LOGGING_MIN_STATUS) {
LogCommands( LogCommands(
"[{}] ([{}]) used command: [{}] (target=[{}])", "[{}] ([{}]) used command: [{}] (target=[{}])",
c->GetName(), c->GetName(),
@@ -507,8 +507,8 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
); );
} }
if (!cur->function) { if (!current_command->function) {
LogError("Command [{}] has a null function", cstr); LogError("Command [{}] has a null function", command);
return -1; return -1;
} }
@@ -525,7 +525,7 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
RecordPlayerEventLogWithClient(c, PlayerEvent::GM_COMMAND, e); RecordPlayerEventLogWithClient(c, PlayerEvent::GM_COMMAND, e);
} }
cur->function(c, &sep); // Dispatch C++ Command current_command->function(c, &sep); // Dispatch C++ Command
return 0; return 0;
} }
@@ -665,7 +665,7 @@ void command_hotfix(Client *c, const Seperator *sep)
hotfix_command = fmt::format("\"{}\" -hotfix={}", shared_memory_path, hotfix_name); hotfix_command = fmt::format("\"{}\" -hotfix={}", shared_memory_path, hotfix_name);
} }
else { else {
hotfix_command = fmt::format("\"{}\"", shared_memory_path, hotfix_name); hotfix_command = fmt::format("\"{}\"", shared_memory_path);
} }
LogInfo("Running hotfix command [{}]", hotfix_command); LogInfo("Running hotfix command [{}]", hotfix_command);
@@ -691,7 +691,7 @@ void command_hotfix(Client *c, const Seperator *sep)
} }
worldserver.SendPacket(&pack); worldserver.SendPacket(&pack);
if (c) { c->Message(Chat::White, "Hotfix applied"); } worldserver.SendEmoteMessage(0, 0, AccountStatus::ApprenticeGuide, Chat::Yellow, "Hotfix applied");
} }
); );
+8 -2
View File
@@ -210,7 +210,7 @@ void Embperl::init_eval_file(void)
"} else {" "} else {"
// we 'my' $filename,$mtime,$package,$sub to prevent them from changing our state up here. // we 'my' $filename,$mtime,$package,$sub to prevent them from changing our state up here.
" eval(\"package $package; my(\\$filename,\\$mtime,\\$package,\\$sub); \\$isloaded = 1; require './$filename'; \");" " eval(\"package $package; my(\\$filename,\\$mtime,\\$package,\\$sub); \\$isloaded = 1; require './$filename'; \");"
// " print $@ if $@;" " print $@ if $@;"
/* "local *FH;open FH, $filename or die \"open '$filename' $!\";" /* "local *FH;open FH, $filename or die \"open '$filename' $!\";"
"local($/) = undef;my $sub = <FH>;close FH;" "local($/) = undef;my $sub = <FH>;close FH;"
"my $eval = qq{package $package; sub handler { $sub; }};" "my $eval = qq{package $package; sub handler { $sub; }};"
@@ -277,8 +277,14 @@ int Embperl::dosub(const char * subname, const std::vector<std::string> * args,
std::string sub = subname; std::string sub = subname;
if (sub == "main::eval_file" && !filename.empty() && File::Exists(filename)) { if (sub == "main::eval_file" && !filename.empty() && File::Exists(filename)) {
BenchTimer benchmark; BenchTimer benchmark;
std::string perl = "perl";
if (File::Exists("/opt/eqemu-perl/bin/perl")) {
perl = "/opt/eqemu-perl/bin/perl";
}
std::string syntax_error = Process::execute( std::string syntax_error = Process::execute(
fmt::format("perl -c {} 2>&1", filename) fmt::format("{} -c {} 2>&1", perl, filename)
); );
LogQuests("Perl eval [{}] took [{}]", filename, benchmark.elapsed()); LogQuests("Perl eval [{}] took [{}]", filename, benchmark.elapsed());
syntax_error = Strings::Trim(syntax_error); syntax_error = Strings::Trim(syntax_error);
+10
View File
@@ -5850,3 +5850,13 @@ void EntityList::DamageArea(
} }
} }
} }
void EntityList::RemoveFromBeacons(Mob *p_mob)
{
for (auto it = beacon_list.begin(); it != beacon_list.end(); ++it) {
if (it->second == p_mob) {
beacon_list.erase(it);
break;
}
}
}
+1
View File
@@ -407,6 +407,7 @@ public:
void RemoveFromTargetsFadingMemories(Mob* spell_target, bool RemoveFromXTargets = false, uint32 max_level = 0); void RemoveFromTargetsFadingMemories(Mob* spell_target, bool RemoveFromXTargets = false, uint32 max_level = 0);
void RemoveFromXTargets(Mob* mob); void RemoveFromXTargets(Mob* mob);
void RemoveFromAutoXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob);
void RemoveFromBeacons(Mob *p_mob);
void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget);
void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone); void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone);
void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true);
+2 -2
View File
@@ -19,7 +19,7 @@ void command_suspend(Client *c, const Seperator *sep)
const std::string reason = sep->arg[3] ? sep->argplus[3] : ""; const std::string reason = sep->arg[3] ? sep->argplus[3] : "";
auto l = AccountRepository::GetWhere( auto l = AccountRepository::GetWhere(
content_db, database,
fmt::format( fmt::format(
"LOWER(charname) = '{}'", "LOWER(charname) = '{}'",
Strings::Escape(character_name) Strings::Escape(character_name)
@@ -41,7 +41,7 @@ void command_suspend(Client *c, const Seperator *sep)
l[0].suspendeduntil = std::time(nullptr) + (days * 86400); l[0].suspendeduntil = std::time(nullptr) + (days * 86400);
l[0].suspend_reason = reason; l[0].suspend_reason = reason;
if (!AccountRepository::UpdateOne(content_db, l[0])) { if (!AccountRepository::UpdateOne(database, l[0])) {
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
+41 -7
View File
@@ -68,6 +68,7 @@
#ifdef _WINDOWS #ifdef _WINDOWS
#else #else
#include <pthread.h> #include <pthread.h>
#include "../common/unix.h" #include "../common/unix.h"
@@ -85,6 +86,8 @@ extern volatile bool is_zone_loaded;
#include "../common/events/player_event_logs.h" #include "../common/events/player_event_logs.h"
#include "../common/path_manager.h" #include "../common/path_manager.h"
#include "../common/database/database_update.h" #include "../common/database/database_update.h"
#include "zone_event_scheduler.h"
#include "zone_cli.h"
EntityList entity_list; EntityList entity_list;
WorldServer worldserver; WorldServer worldserver;
@@ -117,12 +120,18 @@ void CatchSignal(int sig_num);
extern void MapOpcodes(); extern void MapOpcodes();
int main(int argc, char** argv) { int main(int argc, char **argv)
{
RegisterExecutablePlatform(ExePlatformZone); RegisterExecutablePlatform(ExePlatformZone);
LogSys.LoadLogSettingsDefaults(); LogSys.LoadLogSettingsDefaults();
set_exception_handler(); set_exception_handler();
// silence logging if we ran a command
if (ZoneCLI::RanConsoleCommand(argc, argv)) {
LogSys.SilenceConsoleLogging();
}
path.LoadPaths(); path.LoadPaths();
#ifdef USE_MAP_MMFS #ifdef USE_MAP_MMFS
@@ -154,9 +163,11 @@ int main(int argc, char** argv) {
} }
Config = ZoneConfig::get(); Config = ZoneConfig::get();
// static zone booting
const char *zone_name; const char *zone_name;
uint32 instance_id = 0; uint32 instance_id = 0;
std::string z_name; std::string z_name;
if (!ZoneCLI::RanSidecarCommand(argc, argv)) {
if (argc == 4) { if (argc == 4) {
instance_id = Strings::ToInt(argv[3]); instance_id = Strings::ToInt(argv[3]);
worldserver.SetLauncherName(argv[2]); worldserver.SetLauncherName(argv[2]);
@@ -226,6 +237,7 @@ int main(int argc, char** argv) {
worldserver.SetLaunchedName("."); worldserver.SetLaunchedName(".");
worldserver.SetLauncherName("NONE"); worldserver.SetLauncherName("NONE");
} }
}
auto mutex = new Mutex; auto mutex = new Mutex;
@@ -235,7 +247,8 @@ int main(int argc, char** argv) {
Config->DatabaseUsername.c_str(), Config->DatabaseUsername.c_str(),
Config->DatabasePassword.c_str(), Config->DatabasePassword.c_str(),
Config->DatabaseDB.c_str(), Config->DatabaseDB.c_str(),
Config->DatabasePort)) { Config->DatabasePort
)) {
LogError("Cannot continue without a database connection"); LogError("Cannot continue without a database connection");
return 1; return 1;
} }
@@ -280,7 +293,12 @@ int main(int argc, char** argv) {
EQ::InitializeDynamicLookups(); 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) LogSys.SetDatabase(&database)
->SetLogPath(path.GetLogPath()) ->SetLogPath(path.GetLogPath())
->LoadLogDatabaseSettings() ->LoadLogDatabaseSettings()
@@ -455,6 +473,11 @@ int main(int argc, char** argv) {
worldserver.Connect(); worldserver.Connect();
worldserver.SetScheduler(&event_scheduler); 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 Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect
#ifdef EQPROFILE #ifdef EQPROFILE
#ifdef PROFILE_DUMP_TIME #ifdef PROFILE_DUMP_TIME
@@ -558,7 +581,15 @@ int main(int argc, char** argv) {
} }
else { else {
if (worldwasconnected && is_zone_loaded) { 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; worldwasconnected = false;
} }
} }
@@ -614,8 +645,9 @@ int main(int argc, char** argv) {
safe_delete(Config); safe_delete(Config);
if (zone != 0) if (zone != 0) {
Zone::Shutdown(true); Zone::Shutdown(true);
}
//Fix for Linux world server problem. //Fix for Linux world server problem.
safe_delete(task_manager); safe_delete(task_manager);
safe_delete(npc_scale_manager); safe_delete(npc_scale_manager);
@@ -638,7 +670,8 @@ void Shutdown()
EQ::EventLoop::Get().Shutdown(); EQ::EventLoop::Get().Shutdown();
} }
void CatchSignal(int sig_num) { void CatchSignal(int sig_num)
{
#ifdef _WINDOWS #ifdef _WINDOWS
LogInfo("Recieved signal: [{}]", sig_num); LogInfo("Recieved signal: [{}]", sig_num);
#endif #endif
@@ -646,7 +679,8 @@ void CatchSignal(int sig_num) {
} }
/* Update Window Title with relevant information */ /* Update Window Title with relevant information */
void UpdateWindowTitle(char* iNewTitle) { void UpdateWindowTitle(char *iNewTitle)
{
#ifdef _WINDOWS #ifdef _WINDOWS
char tmp[500]; char tmp[500];
if (iNewTitle) { if (iNewTitle) {
+1
View File
@@ -560,6 +560,7 @@ Mob::~Mob()
entity_list.RemoveMobFromCloseLists(this); entity_list.RemoveMobFromCloseLists(this);
entity_list.RemoveAuraFromMobs(this); entity_list.RemoveAuraFromMobs(this);
entity_list.RemoveFromBeacons(this);
close_mobs.clear(); close_mobs.clear();
+18
View 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
);
}
}
@@ -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");
}
}
@@ -0,0 +1,4 @@
void SidecarApi::MapBestZController(const httplib::Request &req, httplib::Response &res)
{
}
+119
View 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
View 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
View 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");
}
+1 -5
View File
@@ -706,11 +706,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_AddFaction: case SE_AddFaction:
{ {
#ifdef SPELL_EFFECT_SPAM if (caster && !IsPet() && GetPrimaryFaction() > 0) {
snprintf(effect_desc, _EDLEN, "Faction Mod: %+i", effect_value);
#endif
// EverHood
if(caster && GetPrimaryFaction()>0) {
caster->AddFactionBonus(GetPrimaryFaction(),effect_value); caster->AddFactionBonus(GetPrimaryFaction(),effect_value);
} }
break; break;
+16 -2
View File
@@ -757,8 +757,22 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
} }
} }
// todo: rule or npc field to auto return normal items also // Regardless of quest or non-quest NPC - No in combat trade completion
if (!quest_npc) // is allowed.
if (tradingWith->CheckAggro(this))
{
for (EQ::ItemInstance* inst : items) {
if (!inst || !inst->GetItem()) {
continue;
}
tradingWith->SayString(TRADE_BACK, GetCleanName());
PushItemOnCursor(*inst, true);
}
}
// Only enforce trade rules if the NPC doesn't have an EVENT_TRADE
// subroutine. That overrides all.
else if (!quest_npc)
{ {
for (EQ::ItemInstance* inst : items) { for (EQ::ItemInstance* inst : items) {
if (!inst || !inst->GetItem()) { if (!inst || !inst->GetItem()) {
+16 -2
View File
@@ -1821,6 +1821,11 @@ void Zone::ResetShutdownTimer() {
autoshutdown_timer.Start(autoshutdown_timer.GetDuration(), true); autoshutdown_timer.Start(autoshutdown_timer.GetDuration(), true);
} }
void Zone::StopShutdownTimer() {
LogInfo("Stopping zone shutdown timer");
autoshutdown_timer.Disable();
}
bool Zone::Depop(bool StartSpawnTimer) { bool Zone::Depop(bool StartSpawnTimer) {
std::map<uint32,NPCType *>::iterator itr; std::map<uint32,NPCType *>::iterator itr;
entity_list.Depop(StartSpawnTimer); entity_list.Depop(StartSpawnTimer);
@@ -2197,12 +2202,21 @@ void Zone::LoadZoneBlockedSpells()
if (zone_total_blocked_spells > 0) { if (zone_total_blocked_spells > 0) {
blocked_spells = new ZoneSpellsBlocked[zone_total_blocked_spells]; blocked_spells = new ZoneSpellsBlocked[zone_total_blocked_spells];
if (!content_db.LoadBlockedSpells(zone_total_blocked_spells, blocked_spells, GetZoneID())) { if (!content_db.LoadBlockedSpells(zone_total_blocked_spells, blocked_spells, GetZoneID())) {
LogError(" Failed to load blocked spells"); LogError(
"Failed to load blocked spells for {} ({}).",
zone_store.GetZoneName(GetZoneID(), true),
GetZoneID()
);
ClearBlockedSpells(); ClearBlockedSpells();
} }
} }
LogInfo("Loaded [{}] blocked spells(s)", Strings::Commify(zone_total_blocked_spells)); LogInfo(
"Loaded [{}] blocked spells(s) for {} ({}).",
Strings::Commify(zone_total_blocked_spells),
zone_store.GetZoneName(GetZoneID(), true),
GetZoneID()
);
} }
} }
+1
View File
@@ -302,6 +302,7 @@ public:
void SpawnConditionChanged(const SpawnCondition &c, int16 old_value); void SpawnConditionChanged(const SpawnCondition &c, int16 old_value);
void StartShutdownTimer(uint32 set_time = (RuleI(Zone, AutoShutdownDelay))); void StartShutdownTimer(uint32 set_time = (RuleI(Zone, AutoShutdownDelay)));
void ResetShutdownTimer(); void ResetShutdownTimer();
void StopShutdownTimer();
void UpdateQGlobal(uint32 qid, QGlobal newGlobal); void UpdateQGlobal(uint32 qid, QGlobal newGlobal);
void weatherSend(Client *client = nullptr); void weatherSend(Client *client = nullptr);
void ClearSpawnTimers(); void ClearSpawnTimers();
+32
View 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
View 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
+39 -32
View File
@@ -11,6 +11,7 @@
#include "zone.h" #include "zone.h"
#include "zonedb.h" #include "zonedb.h"
#include "aura.h" #include "aura.h"
#include "../common/repositories/blocked_spells_repository.h"
#include "../common/repositories/character_tribute_repository.h" #include "../common/repositories/character_tribute_repository.h"
#include "../common/repositories/character_disciplines_repository.h" #include "../common/repositories/character_disciplines_repository.h"
#include "../common/repositories/npc_types_repository.h" #include "../common/repositories/npc_types_repository.h"
@@ -19,6 +20,7 @@
#include "../common/repositories/character_pet_inventory_repository.h" #include "../common/repositories/character_pet_inventory_repository.h"
#include "../common/repositories/character_pet_info_repository.h" #include "../common/repositories/character_pet_info_repository.h"
#include "../common/repositories/character_buffs_repository.h" #include "../common/repositories/character_buffs_repository.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include <ctime> #include <ctime>
#include <iostream> #include <iostream>
@@ -2845,49 +2847,54 @@ uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) {
return Strings::ToInt(row[0]); return Strings::ToInt(row[0]);
} }
int32 ZoneDatabase::GetBlockedSpellsCount(uint32 zoneid) int64 ZoneDatabase::GetBlockedSpellsCount(uint32 zone_id)
{ {
std::string query = StringFormat("SELECT count(*) FROM blocked_spells WHERE zoneid = %d", zoneid); return BlockedSpellsRepository::Count(
auto results = QueryDatabase(query); *this,
if (!results.Success()) { fmt::format(
return -1; "zoneid = {} {}",
zone_id,
ContentFilterCriteria::apply()
)
);
} }
if (results.RowCount() == 0) bool ZoneDatabase::LoadBlockedSpells(int64 blocked_spells_count, ZoneSpellsBlocked* into, uint32 zone_id)
return -1;
auto& row = results.begin();
return Strings::ToInt(row[0]);
}
bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid)
{ {
LogInfo("Loading Blocked Spells from database"); LogInfo("Loading Blocked Spells from database for {} ({}).", zone_store.GetZoneName(zone_id, true), zone_id);
std::string query = StringFormat("SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " const auto& l = BlockedSpellsRepository::GetWhere(
"FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid); *this,
auto results = QueryDatabase(query); fmt::format(
if (!results.Success()) { "zoneid = {} {} ORDER BY id ASC",
return false; zone_id,
} ContentFilterCriteria::apply()
)
);
if (results.RowCount() == 0) if (l.empty()) {
return true; return true;
}
int32 index = 0; int64 i = 0;
for(auto& row = results.begin(); row != results.end(); ++row, ++index) {
if(index >= blockedSpellsCount) { for (const auto& e : l) {
std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl; if (i >= blocked_spells_count) {
LogError(
"Blocked spells count of {} exceeded for {} ({}).",
blocked_spells_count,
zone_store.GetZoneName(zone_id, true),
zone_id
);
break; break;
} }
memset(&into[index], 0, sizeof(ZoneSpellsBlocked)); memset(&into[i], 0, sizeof(ZoneSpellsBlocked));
into[index].spellid = Strings::ToInt(row[1]); into[i].spellid = e.spellid;
into[index].type = Strings::ToInt(row[2]); into[i].type = e.type;
into[index].m_Location = glm::vec3(Strings::ToFloat(row[3]), Strings::ToFloat(row[4]), Strings::ToFloat(row[5])); into[i].m_Location = glm::vec3(e.x, e.y, e.z);
into[index].m_Difference = glm::vec3(Strings::ToFloat(row[6]), Strings::ToFloat(row[7]), Strings::ToFloat(row[8])); into[i].m_Difference = glm::vec3(e.x_diff, e.y_diff, e.z_diff);
strn0cpy(into[index].message, row[9], 255); strn0cpy(into[i].message, e.message.c_str(), sizeof(into[i].message));
} }
return true; return true;
+2 -2
View File
@@ -619,8 +619,8 @@ public:
int GetDoorsDBCountPlusOne(std::string zone_short_name, int16 version); int GetDoorsDBCountPlusOne(std::string zone_short_name, int16 version);
/* Blocked Spells */ /* Blocked Spells */
int32 GetBlockedSpellsCount(uint32 zoneid); int64 GetBlockedSpellsCount(uint32 zone_id);
bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid); bool LoadBlockedSpells(int64 blocked_spells_count, ZoneSpellsBlocked* into, uint32 zone_id);
/* Traps */ /* Traps */
bool LoadTraps(const char* zonename, int16 version); bool LoadTraps(const char* zonename, int16 version);