mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 13:12:28 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04d6f8feea | |||
| dfc1bf0381 | |||
| 2237c3a056 | |||
| 4af191c593 | |||
| 0a3972deb9 | |||
| 9d2f258390 | |||
| 0b452f4ec1 | |||
| fef629e1df | |||
| a5a51fbe44 | |||
| 47db92cdb6 | |||
| 690301e80d | |||
| 1887e48b76 | |||
| af2691eb12 | |||
| 2df4289588 | |||
| 49d4d0acc3 | |||
| 5a663910a5 | |||
| b027edd21e | |||
| 0bbfcf7adc | |||
| 7962a0bd38 | |||
| 4d9b51df0a | |||
| 508ecec6ea | |||
| f0c6fa2a26 | |||
| ad6dbb7beb | |||
| 6ddbb41617 | |||
| 8a558f6a29 | |||
| 0585be0360 | |||
| 6927baef7f | |||
| 52d64781b5 | |||
| 0667fe435f | |||
| 9959070f24 | |||
| 2a91f08845 | |||
| adc64005f1 | |||
| 605480f1c4 | |||
| 3b95601c62 | |||
| a4f2ed28f1 |
+1
-1
@@ -15,7 +15,7 @@ volumes:
|
||||
|
||||
steps:
|
||||
- name: Build Linux X64
|
||||
image: akkadius/eqemu-server:v11
|
||||
image: akkadius/eqemu-server:v13
|
||||
environment:
|
||||
GITHUB_TOKEN:
|
||||
from_secret: GH_RELEASE_GITHUB_API_TOKEN
|
||||
|
||||
+106
@@ -1,3 +1,109 @@
|
||||
## [22.31.2] - 10/31/2023
|
||||
|
||||
### Fixes
|
||||
|
||||
* Hotfix issue with beacon spells crashing @Akkadius 2023-10-31
|
||||
|
||||
## [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
|
||||
|
||||
### Build
|
||||
|
||||
* Perl Linux build fix
|
||||
|
||||
## [22.28.0] - 10/15/2023
|
||||
|
||||
### Bots
|
||||
|
||||
+16
-1
@@ -23,7 +23,12 @@ IF (EQEMU_BUILD_STATIC)
|
||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a")
|
||||
MESSAGE(STATUS "Building with static linking")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
|
||||
ENDIF(EQEMU_BUILD_STATIC)
|
||||
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)
|
||||
|
||||
IF(MSVC)
|
||||
ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
|
||||
@@ -133,6 +138,13 @@ ELSE()
|
||||
MESSAGE(STATUS "* mbedTLS: MISSING *")
|
||||
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 "**************************************************")
|
||||
|
||||
#options
|
||||
@@ -389,6 +401,9 @@ IF(PERL_LIBRARY_ENABLED)
|
||||
ADD_DEFINITIONS(-DEMBPERL)
|
||||
ADD_DEFINITIONS(-DEMBPERL_PLUGIN)
|
||||
ADD_DEFINITIONS(-DPERLBIND_NO_STRICT_SCALAR_TYPES)
|
||||
IF (UNIX AND EQEMU_BUILD_STATIC)
|
||||
SET(SERVER_LIBS ${SERVER_LIBS} libcrypt.a)
|
||||
ENDIF ()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -4956,6 +4956,45 @@ ALTER TABLE `items`
|
||||
MODIFY COLUMN `augdistiller` INT(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `scrollunk1` INT(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `bardeffect` MEDIUMINT(6) NOT NULL DEFAULT 0;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9238,
|
||||
.description = "2023_10_18_tradeskill_add_learned_by_item_id.sql",
|
||||
.check = "SHOW COLUMNS FROM `tradeskill_recipe` LIKE 'learned_by_item_id'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `tradeskill_recipe`
|
||||
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`);
|
||||
)"
|
||||
},
|
||||
|
||||
|
||||
@@ -61,6 +61,29 @@ DROP TABLE IF EXISTS `bot_group_members`;
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
)",
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9040,
|
||||
.description = "2023_11_16_bot_starting_items.sql",
|
||||
.check = "SHOW TABLES LIKE 'bot_starting_items'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
CREATE TABLE `bot_starting_items` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`races` int(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`classes` int(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`item_id` int(11) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`item_charges` tinyint(3) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`min_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`slot_id` mediumint(9) NOT NULL DEFAULT -1,
|
||||
`min_expansion` tinyint(4) NOT NULL DEFAULT -1,
|
||||
`max_expansion` tinyint(4) NOT NULL DEFAULT -1,
|
||||
`content_flags` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
|
||||
`content_flags_disabled` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci;
|
||||
)"
|
||||
}
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
+24640
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 "";
|
||||
}
|
||||
|
||||
+2
-1
@@ -37,7 +37,8 @@ enum EQEmuExePlatform
|
||||
ExePlatformClientImport,
|
||||
ExePlatformClientExport,
|
||||
ExePlatformHC,
|
||||
ExePlatformTests
|
||||
ExePlatformTests,
|
||||
ExePlatformZoneSidecar
|
||||
};
|
||||
|
||||
void RegisterExecutablePlatform(EQEmuExePlatform p);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,414 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_BOT_STARTING_ITEMS_REPOSITORY_H
|
||||
#define EQEMU_BASE_BOT_STARTING_ITEMS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseBotStartingItemsRepository {
|
||||
public:
|
||||
struct BotStartingItems {
|
||||
uint32_t id;
|
||||
uint32_t races;
|
||||
uint32_t classes;
|
||||
uint32_t item_id;
|
||||
uint8_t item_charges;
|
||||
int32_t slot_id;
|
||||
int8_t min_expansion;
|
||||
int8_t max_expansion;
|
||||
std::string content_flags;
|
||||
std::string content_flags_disabled;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"races",
|
||||
"classes",
|
||||
"item_id",
|
||||
"item_charges",
|
||||
"slot_id",
|
||||
"min_expansion",
|
||||
"max_expansion",
|
||||
"content_flags",
|
||||
"content_flags_disabled",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"races",
|
||||
"classes",
|
||||
"item_id",
|
||||
"item_charges",
|
||||
"slot_id",
|
||||
"min_expansion",
|
||||
"max_expansion",
|
||||
"content_flags",
|
||||
"content_flags_disabled",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("bot_starting_items");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static BotStartingItems NewEntity()
|
||||
{
|
||||
BotStartingItems e{};
|
||||
|
||||
e.id = 0;
|
||||
e.races = 0;
|
||||
e.classes = 0;
|
||||
e.item_id = 0;
|
||||
e.item_charges = 1;
|
||||
e.slot_id = -1;
|
||||
e.min_expansion = -1;
|
||||
e.max_expansion = -1;
|
||||
e.content_flags = "";
|
||||
e.content_flags_disabled = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static BotStartingItems GetBotStartingItems(
|
||||
const std::vector<BotStartingItems> &bot_starting_itemss,
|
||||
int bot_starting_items_id
|
||||
)
|
||||
{
|
||||
for (auto &bot_starting_items : bot_starting_itemss) {
|
||||
if (bot_starting_items.id == bot_starting_items_id) {
|
||||
return bot_starting_items;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static BotStartingItems FindOne(
|
||||
Database& db,
|
||||
int bot_starting_items_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
bot_starting_items_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
BotStartingItems e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.races = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
|
||||
e.classes = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.item_id = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
e.item_charges = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.slot_id = static_cast<int32_t>(atoi(row[5]));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[6]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[7]));
|
||||
e.content_flags = row[8] ? row[8] : "";
|
||||
e.content_flags_disabled = row[9] ? row[9] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int bot_starting_items_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
bot_starting_items_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const BotStartingItems &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.races));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.classes));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.item_charges));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.slot_id));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.min_expansion));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.max_expansion));
|
||||
v.push_back(columns[8] + " = '" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back(columns[9] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static BotStartingItems InsertOne(
|
||||
Database& db,
|
||||
BotStartingItems e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.races));
|
||||
v.push_back(std::to_string(e.classes));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_charges));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.min_expansion));
|
||||
v.push_back(std::to_string(e.max_expansion));
|
||||
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<BotStartingItems> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.races));
|
||||
v.push_back(std::to_string(e.classes));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_charges));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.min_expansion));
|
||||
v.push_back(std::to_string(e.max_expansion));
|
||||
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<BotStartingItems> All(Database& db)
|
||||
{
|
||||
std::vector<BotStartingItems> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotStartingItems e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.races = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
|
||||
e.classes = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.item_id = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
e.item_charges = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.slot_id = static_cast<int32_t>(atoi(row[5]));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[6]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[7]));
|
||||
e.content_flags = row[8] ? row[8] : "";
|
||||
e.content_flags_disabled = row[9] ? row[9] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BotStartingItems> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<BotStartingItems> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotStartingItems e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.races = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
|
||||
e.classes = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.item_id = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
e.item_charges = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.slot_id = static_cast<int32_t>(atoi(row[5]));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[6]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[7]));
|
||||
e.content_flags = row[8] ? row[8] : "";
|
||||
e.content_flags_disabled = row[9] ? row[9] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int64 GetMaxId(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||
PrimaryKey(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COUNT(*) FROM {} {}",
|
||||
TableName(),
|
||||
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_BOT_STARTING_ITEMS_REPOSITORY_H
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef EQEMU_BOT_STARTING_ITEMS_REPOSITORY_H
|
||||
#define EQEMU_BOT_STARTING_ITEMS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_bot_starting_items_repository.h"
|
||||
|
||||
class BotStartingItemsRepository: public BaseBotStartingItemsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* BotStartingItemsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* BotStartingItemsRepository::GetWhereNeverExpires()
|
||||
* BotStartingItemsRepository::GetWhereXAndY()
|
||||
* BotStartingItemsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BOT_STARTING_ITEMS_REPOSITORY_H
|
||||
@@ -531,6 +531,7 @@ RULE_BOOL(Combat, BackstabIgnoresBane, false, "Enable or disable Bane weapon dam
|
||||
RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a player when already in melee range of the summoner.")
|
||||
RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for AutoFire LoS to pass.")
|
||||
RULE_INT(Combat, ExtraAllowedKickClassesBitmask, 0, "Bitmask for allowing extra classes beyond Warrior, Ranger, Beastlord, and Berserker to kick, No Extra Classes (0) by default")
|
||||
RULE_INT(Combat, MaxProcs, 4, "Adjustable maximum number of procs per round, the hard cap is MAX_PROCS (11). Requires mob repop or client zone when changed")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(NPC)
|
||||
|
||||
+23
-16
@@ -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, ...);
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// 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.2-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,9 +42,9 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9237
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9240
|
||||
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9039
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9040
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.28.0",
|
||||
"version": "22.31.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -156,6 +156,11 @@ bool UCSDatabase::VerifyMailKey(const std::string& characterName, int IPAddress,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
LogInfo("No mailkeys found for [{}].", characterName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -9,7 +9,17 @@ git submodule init && git submodule update
|
||||
|
||||
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 -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
|
||||
./bin/tests
|
||||
|
||||
+5
-1
@@ -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})
|
||||
|
||||
|
||||
+10
-7
@@ -3628,7 +3628,7 @@ int64 Mob::ReduceAllDamage(int64 damage)
|
||||
|
||||
bool Mob::HasProcs() const
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (IsValidSpell(PermaProcs[i].spellID) || IsValidSpell(SpellProcs[i].spellID)) {
|
||||
return true;
|
||||
}
|
||||
@@ -3646,7 +3646,7 @@ bool Mob::HasProcs() const
|
||||
|
||||
bool Mob::HasDefensiveProcs() const
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (IsValidSpell(DefensiveProcs[i].spellID)) {
|
||||
return true;
|
||||
}
|
||||
@@ -3682,7 +3682,7 @@ bool Mob::HasSkillProcSuccess() const
|
||||
|
||||
bool Mob::HasRangedProcs() const
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++){
|
||||
for (int i = 0; i < m_max_procs; i++){
|
||||
if (IsValidSpell(RangedProcs[i].spellID)) {
|
||||
return true;
|
||||
}
|
||||
@@ -4580,7 +4580,7 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand)
|
||||
}
|
||||
|
||||
//Spell Procs and Quest added procs
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (IsValidSpell(DefensiveProcs[i].spellID)) {
|
||||
if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, ProcType::DEFENSIVE_PROC)) {
|
||||
float chance = proc_chance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f);
|
||||
@@ -4783,7 +4783,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
|
||||
|
||||
int16 poison_slot=-1;
|
||||
|
||||
for (uint32 i = 0; i < MAX_PROCS; i++) {
|
||||
for (uint32 i = 0; i < m_max_procs; i++) {
|
||||
if (IsPet() && hand != EQ::invslot::slotPrimary) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
|
||||
continue; // If pets ever can proc from off hand, this will need to change
|
||||
|
||||
@@ -5212,8 +5212,11 @@ bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
|
||||
FB_Level = itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
|
||||
|
||||
// modern AA description says rank 1 (500) is 50% chance
|
||||
int ProcChance =
|
||||
aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE];
|
||||
int ProcChance = (
|
||||
aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] +
|
||||
itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] +
|
||||
spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE]
|
||||
);
|
||||
|
||||
if (FB_Level && FB_Dmg && (defender->GetLevel() <= FB_Level) &&
|
||||
(ProcChance >= zone->random.Int(1, 1000))) {
|
||||
|
||||
+53
-2
@@ -22,8 +22,11 @@
|
||||
#include "doors.h"
|
||||
#include "quest_parser_collection.h"
|
||||
#include "lua_parser.h"
|
||||
#include "../common/repositories/bot_inventories_repository.h"
|
||||
#include "../common/repositories/bot_spell_settings_repository.h"
|
||||
#include "../common/repositories/bot_starting_items_repository.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
|
||||
// This constructor is used during the bot create command
|
||||
Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) {
|
||||
@@ -3333,7 +3336,8 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
||||
|
||||
if (auto raid = entity_list.GetRaidByBotName(GetName())) {
|
||||
// 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);
|
||||
} else {
|
||||
SetRaidGrouped(true);
|
||||
@@ -3343,7 +3347,8 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
||||
}
|
||||
else if (auto group = entity_list.GetGroupByMobName(GetName())) {
|
||||
// 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);
|
||||
} else {
|
||||
SetGrouped(true);
|
||||
@@ -8727,4 +8732,50 @@ bool Bot::CheckSpawnConditions(Client* c) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bot::AddBotStartingItems(uint16 race_id, uint8 class_id)
|
||||
{
|
||||
if (!IsPlayerRace(race_id) || !IsPlayerClass(class_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16 race_bitmask = GetPlayerRaceBit(race_id);
|
||||
const uint16 class_bitmask = GetPlayerClassBit(class_id);
|
||||
|
||||
const auto& l = BotStartingItemsRepository::GetWhere(
|
||||
content_db,
|
||||
fmt::format(
|
||||
"(races & {} OR races = 0) AND "
|
||||
"(classes & {} OR classes = 0) {}",
|
||||
race_bitmask,
|
||||
class_bitmask,
|
||||
ContentFilterCriteria::apply()
|
||||
)
|
||||
);
|
||||
|
||||
if (l.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<BotInventoriesRepository::BotInventories> v;
|
||||
|
||||
for (const auto& e : l) {
|
||||
if (
|
||||
CanClassEquipItem(e.item_id) &&
|
||||
(CanRaceEquipItem(e.item_id) || RuleB(Bots, AllowBotEquipAnyRaceGear))
|
||||
) {
|
||||
auto i = BotInventoriesRepository::NewEntity();
|
||||
i.bot_id = GetBotID();
|
||||
i.slot_id = e.slot_id;
|
||||
i.item_id = e.item_id;
|
||||
i.inst_charges = e.item_charges;
|
||||
|
||||
v.emplace_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!v.empty()) {
|
||||
BotInventoriesRepository::InsertMany(content_db, v);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||
|
||||
@@ -460,6 +460,8 @@ public:
|
||||
uint8 gender
|
||||
);
|
||||
|
||||
void AddBotStartingItems(uint16 race_id, uint8 class_id);
|
||||
|
||||
// Static Bot Group Methods
|
||||
static bool AddBotToGroup(Bot* bot, Group* group);
|
||||
static bool RemoveBotFromGroup(Bot* bot, Group* group);
|
||||
|
||||
@@ -9041,6 +9041,8 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
|
||||
parse->EventPlayer(EVENT_BOT_CREATE, bot_owner, export_string, 0);
|
||||
}
|
||||
|
||||
my_bot->AddBotStartingItems(bot_race, bot_class);
|
||||
|
||||
safe_delete(my_bot);
|
||||
|
||||
return bot_id;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
auto cur = commandlist[cstr];
|
||||
const CommandRecord* current_command = commandlist[command];
|
||||
|
||||
bool is_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;
|
||||
|
||||
if (arguments >= 2) {
|
||||
if (arguments) {
|
||||
const std::string& sub_command = sep.arg[1];
|
||||
|
||||
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);
|
||||
is_subcommand = true;
|
||||
found_subcommand_setting = true;
|
||||
@@ -465,7 +465,7 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
|
||||
|
||||
if (!found_subcommand_setting) {
|
||||
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);
|
||||
is_subcommand = true;
|
||||
break;
|
||||
@@ -475,7 +475,7 @@ int command_realdispatch(Client *c, std::string message, bool 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.");
|
||||
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);
|
||||
}
|
||||
|
||||
if (cur->admin >= COMMANDS_LOGGING_MIN_STATUS) {
|
||||
if (current_command->admin >= COMMANDS_LOGGING_MIN_STATUS) {
|
||||
LogCommands(
|
||||
"[{}] ([{}]) used command: [{}] (target=[{}])",
|
||||
c->GetName(),
|
||||
@@ -507,8 +507,8 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
|
||||
);
|
||||
}
|
||||
|
||||
if (!cur->function) {
|
||||
LogError("Command [{}] has a null function", cstr);
|
||||
if (!current_command->function) {
|
||||
LogError("Command [{}] has a null function", command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -525,7 +525,7 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
|
||||
RecordPlayerEventLogWithClient(c, PlayerEvent::GM_COMMAND, e);
|
||||
}
|
||||
|
||||
cur->function(c, &sep); // Dispatch C++ Command
|
||||
current_command->function(c, &sep); // Dispatch C++ Command
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -665,7 +665,7 @@ void command_hotfix(Client *c, const Seperator *sep)
|
||||
hotfix_command = fmt::format("\"{}\" -hotfix={}", shared_memory_path, hotfix_name);
|
||||
}
|
||||
else {
|
||||
hotfix_command = fmt::format("\"{}\"", shared_memory_path, hotfix_name);
|
||||
hotfix_command = fmt::format("\"{}\"", shared_memory_path);
|
||||
}
|
||||
|
||||
LogInfo("Running hotfix command [{}]", hotfix_command);
|
||||
@@ -691,7 +691,7 @@ void command_hotfix(Client *c, const Seperator *sep)
|
||||
}
|
||||
worldserver.SendPacket(&pack);
|
||||
|
||||
if (c) { c->Message(Chat::White, "Hotfix applied"); }
|
||||
worldserver.SendEmoteMessage(0, 0, AccountStatus::ApprenticeGuide, Chat::Yellow, "Hotfix applied");
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
+8
-2
@@ -210,7 +210,7 @@ void Embperl::init_eval_file(void)
|
||||
"} else {"
|
||||
// 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'; \");"
|
||||
// " print $@ if $@;"
|
||||
" print $@ if $@;"
|
||||
/* "local *FH;open FH, $filename or die \"open '$filename' $!\";"
|
||||
"local($/) = undef;my $sub = <FH>;close FH;"
|
||||
"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;
|
||||
if (sub == "main::eval_file" && !filename.empty() && File::Exists(filename)) {
|
||||
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(
|
||||
fmt::format("perl -c {} 2>&1", filename)
|
||||
fmt::format("{} -c {} 2>&1", perl, filename)
|
||||
);
|
||||
LogQuests("Perl eval [{}] took [{}]", filename, benchmark.elapsed());
|
||||
syntax_error = Strings::Trim(syntax_error);
|
||||
|
||||
@@ -19,7 +19,7 @@ void command_suspend(Client *c, const Seperator *sep)
|
||||
const std::string reason = sep->arg[3] ? sep->argplus[3] : "";
|
||||
|
||||
auto l = AccountRepository::GetWhere(
|
||||
content_db,
|
||||
database,
|
||||
fmt::format(
|
||||
"LOWER(charname) = '{}'",
|
||||
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].suspend_reason = reason;
|
||||
|
||||
if (!AccountRepository::UpdateOne(content_db, l[0])) {
|
||||
if (!AccountRepository::UpdateOne(database, l[0])) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
|
||||
+115
-81
@@ -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) {
|
||||
|
||||
+5
-1
@@ -288,8 +288,11 @@ Mob::Mob(
|
||||
|
||||
feigned = false;
|
||||
|
||||
int max_procs = MAX_PROCS;
|
||||
m_max_procs = std::min(RuleI(Combat, MaxProcs), max_procs);
|
||||
|
||||
// clear the proc arrays
|
||||
for (int j = 0; j < MAX_PROCS; j++) {
|
||||
for (int j = 0; j < m_max_procs; j++) {
|
||||
PermaProcs[j].spellID = SPELL_UNKNOWN;
|
||||
PermaProcs[j].chance = 0;
|
||||
PermaProcs[j].base_spellID = SPELL_UNKNOWN;
|
||||
@@ -511,6 +514,7 @@ Mob::Mob(
|
||||
SetCanOpenDoors(true);
|
||||
|
||||
is_boat = IsBoat();
|
||||
|
||||
}
|
||||
|
||||
Mob::~Mob()
|
||||
|
||||
+4
-1
@@ -1459,6 +1459,9 @@ protected:
|
||||
|
||||
Timer m_z_clip_check_timer;
|
||||
|
||||
// dynamically set via memory on constructor
|
||||
int8 m_max_procs = 0;
|
||||
|
||||
virtual bool AI_EngagedCastCheck() { return(false); }
|
||||
virtual bool AI_PursueCastCheck() { return(false); }
|
||||
virtual bool AI_IdleCastCheck() { return(false); }
|
||||
@@ -1600,7 +1603,7 @@ protected:
|
||||
int16 GetSympatheticSpellProcRate(uint16 spell_id);
|
||||
uint16 GetSympatheticSpellProcID(uint16 spell_id);
|
||||
|
||||
enum {MAX_PROCS = 4};
|
||||
enum {MAX_PROCS = 10};
|
||||
tProc PermaProcs[MAX_PROCS];
|
||||
tProc SpellProcs[MAX_PROCS];
|
||||
tProc DefensiveProcs[MAX_PROCS];
|
||||
|
||||
@@ -2650,6 +2650,8 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
|
||||
).c_str()
|
||||
);
|
||||
} else {
|
||||
new_bot->AddBotStartingItems(race, botclass);
|
||||
|
||||
initiator->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
@@ -706,11 +706,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
case SE_AddFaction:
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Faction Mod: %+i", effect_value);
|
||||
#endif
|
||||
// EverHood
|
||||
if(caster && GetPrimaryFaction()>0) {
|
||||
if (caster && !IsPet() && GetPrimaryFaction() > 0) {
|
||||
caster->AddFactionBonus(GetPrimaryFaction(),effect_value);
|
||||
}
|
||||
break;
|
||||
|
||||
+9
-9
@@ -6017,7 +6017,7 @@ bool Mob::IsCombatProc(uint16 spell_id) {
|
||||
/*
|
||||
Procs that originate from casted spells are still limited by SPA 311 (~Kayen confirmed on live 2/4/22)
|
||||
*/
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (PermaProcs[i].spellID == spell_id ||
|
||||
SpellProcs[i].spellID == spell_id ||
|
||||
RangedProcs[i].spellID == spell_id ||
|
||||
@@ -6044,7 +6044,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
|
||||
|
||||
int i;
|
||||
if (bPerma) {
|
||||
for (i = 0; i < MAX_PROCS; i++) {
|
||||
for (i = 0; i < m_max_procs; i++) {
|
||||
if (!IsValidSpell(PermaProcs[i].spellID)) {
|
||||
PermaProcs[i].spellID = spell_id;
|
||||
PermaProcs[i].chance = iChance;
|
||||
@@ -6059,7 +6059,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
|
||||
} else {
|
||||
// If its a poison proc, replace any existing one if present.
|
||||
if (base_spell_id == POISON_PROC) {
|
||||
for (i = 0; i < MAX_PROCS; i++) {
|
||||
for (i = 0; i < m_max_procs; i++) {
|
||||
// If we already have a poison proc active replace it and return
|
||||
if (SpellProcs[i].base_spellID == POISON_PROC) {
|
||||
SpellProcs[i].spellID = spell_id;
|
||||
@@ -6076,7 +6076,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
|
||||
// or it is poison and no poison procs are currently present.
|
||||
// Find a slot and use it as normal.
|
||||
|
||||
for (i = 0; i < MAX_PROCS; i++) {
|
||||
for (i = 0; i < m_max_procs; i++) {
|
||||
if (!IsValidSpell(SpellProcs[i].spellID)) {
|
||||
SpellProcs[i].spellID = spell_id;
|
||||
SpellProcs[i].chance = iChance;
|
||||
@@ -6093,7 +6093,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
|
||||
}
|
||||
|
||||
bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) {
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (bAll || SpellProcs[i].spellID == spell_id) {
|
||||
SpellProcs[i].spellID = SPELL_UNKNOWN;
|
||||
SpellProcs[i].chance = 0;
|
||||
@@ -6112,7 +6112,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id
|
||||
return(false);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < MAX_PROCS; i++) {
|
||||
for (i = 0; i < m_max_procs; i++) {
|
||||
if (!IsValidSpell(DefensiveProcs[i].spellID)) {
|
||||
DefensiveProcs[i].spellID = spell_id;
|
||||
DefensiveProcs[i].chance = iChance;
|
||||
@@ -6128,7 +6128,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id
|
||||
|
||||
bool Mob::RemoveDefensiveProc(uint16 spell_id, bool bAll)
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (bAll || DefensiveProcs[i].spellID == spell_id) {
|
||||
DefensiveProcs[i].spellID = SPELL_UNKNOWN;
|
||||
DefensiveProcs[i].chance = 0;
|
||||
@@ -6146,7 +6146,7 @@ bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id, u
|
||||
return(false);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < MAX_PROCS; i++) {
|
||||
for (i = 0; i < m_max_procs; i++) {
|
||||
if (!IsValidSpell(RangedProcs[i].spellID)) {
|
||||
RangedProcs[i].spellID = spell_id;
|
||||
RangedProcs[i].chance = iChance;
|
||||
@@ -6162,7 +6162,7 @@ bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id, u
|
||||
|
||||
bool Mob::RemoveRangedProc(uint16 spell_id, bool bAll)
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
for (int i = 0; i < m_max_procs; i++) {
|
||||
if (bAll || RangedProcs[i].spellID == spell_id) {
|
||||
RangedProcs[i].spellID = SPELL_UNKNOWN;
|
||||
RangedProcs[i].chance = 0;
|
||||
|
||||
+16
-2
@@ -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
|
||||
if (!quest_npc)
|
||||
// Regardless of quest or non-quest NPC - No in combat trade completion
|
||||
// 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) {
|
||||
if (!inst || !inst->GetItem()) {
|
||||
|
||||
+16
-2
@@ -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);
|
||||
@@ -2197,12 +2202,21 @@ void Zone::LoadZoneBlockedSpells()
|
||||
if (zone_total_blocked_spells > 0) {
|
||||
blocked_spells = new ZoneSpellsBlocked[zone_total_blocked_spells];
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
+42
-35
@@ -11,6 +11,7 @@
|
||||
#include "zone.h"
|
||||
#include "zonedb.h"
|
||||
#include "aura.h"
|
||||
#include "../common/repositories/blocked_spells_repository.h"
|
||||
#include "../common/repositories/character_tribute_repository.h"
|
||||
#include "../common/repositories/character_disciplines_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_info_repository.h"
|
||||
#include "../common/repositories/character_buffs_repository.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
@@ -2845,50 +2847,55 @@ uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) {
|
||||
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);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0)
|
||||
return -1;
|
||||
|
||||
auto& row = results.begin();
|
||||
|
||||
return Strings::ToInt(row[0]);
|
||||
return BlockedSpellsRepository::Count(
|
||||
*this,
|
||||
fmt::format(
|
||||
"zoneid = {} {}",
|
||||
zone_id,
|
||||
ContentFilterCriteria::apply()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid)
|
||||
bool ZoneDatabase::LoadBlockedSpells(int64 blocked_spells_count, ZoneSpellsBlocked* into, uint32 zone_id)
|
||||
{
|
||||
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 "
|
||||
"FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
const auto& l = BlockedSpellsRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"zoneid = {} {} ORDER BY id ASC",
|
||||
zone_id,
|
||||
ContentFilterCriteria::apply()
|
||||
)
|
||||
);
|
||||
|
||||
if (results.RowCount() == 0)
|
||||
if (l.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 index = 0;
|
||||
for(auto& row = results.begin(); row != results.end(); ++row, ++index) {
|
||||
if(index >= blockedSpellsCount) {
|
||||
std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl;
|
||||
break;
|
||||
}
|
||||
int64 i = 0;
|
||||
|
||||
memset(&into[index], 0, sizeof(ZoneSpellsBlocked));
|
||||
into[index].spellid = Strings::ToInt(row[1]);
|
||||
into[index].type = Strings::ToInt(row[2]);
|
||||
into[index].m_Location = glm::vec3(Strings::ToFloat(row[3]), Strings::ToFloat(row[4]), Strings::ToFloat(row[5]));
|
||||
into[index].m_Difference = glm::vec3(Strings::ToFloat(row[6]), Strings::ToFloat(row[7]), Strings::ToFloat(row[8]));
|
||||
strn0cpy(into[index].message, row[9], 255);
|
||||
}
|
||||
for (const auto& e : l) {
|
||||
if (i >= blocked_spells_count) {
|
||||
LogError(
|
||||
"Blocked spells count of {} exceeded for {} ({}).",
|
||||
blocked_spells_count,
|
||||
zone_store.GetZoneName(zone_id, true),
|
||||
zone_id
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&into[i], 0, sizeof(ZoneSpellsBlocked));
|
||||
into[i].spellid = e.spellid;
|
||||
into[i].type = e.type;
|
||||
into[i].m_Location = glm::vec3(e.x, e.y, e.z);
|
||||
into[i].m_Difference = glm::vec3(e.x_diff, e.y_diff, e.z_diff);
|
||||
strn0cpy(into[i].message, e.message.c_str(), sizeof(into[i].message));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+2
-2
@@ -619,8 +619,8 @@ public:
|
||||
int GetDoorsDBCountPlusOne(std::string zone_short_name, int16 version);
|
||||
|
||||
/* Blocked Spells */
|
||||
int32 GetBlockedSpellsCount(uint32 zoneid);
|
||||
bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid);
|
||||
int64 GetBlockedSpellsCount(uint32 zone_id);
|
||||
bool LoadBlockedSpells(int64 blocked_spells_count, ZoneSpellsBlocked* into, uint32 zone_id);
|
||||
|
||||
/* Traps */
|
||||
bool LoadTraps(const char* zonename, int16 version);
|
||||
|
||||
Reference in New Issue
Block a user