mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-25 19:22:27 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d41725e325 | |||
| 88580b69b6 | |||
| f7a6fe595a | |||
| 07d14c2681 | |||
| eac7a73fb6 | |||
| c5715f1f14 | |||
| 3902230fa1 | |||
| 212969f5cd | |||
| de4226fdc9 | |||
| 8b13434197 | |||
| 27274397ec | |||
| 2b79a36014 | |||
| 8cf52294e9 | |||
| 0ef79903f8 | |||
| ab14458f9e | |||
| 5b94e736b3 | |||
| c3b8cc9744 | |||
| 89e3b2c72e | |||
| 23c4aa241b | |||
| acb7584e26 | |||
| a885bd9322 | |||
| 20fe1926e0 | |||
| eb7118754b | |||
| 3611b49f68 | |||
| 511d8a8bb3 | |||
| 1598d2e17b | |||
| 805757ba87 | |||
| eb6ac25540 | |||
| cb634cf57d | |||
| 2f7ca2cdc8 | |||
| 425d24c1f4 | |||
| 875df8e64a | |||
| 7a2d2a0c51 | |||
| 5296202e56 | |||
| e2db8ffea8 | |||
| fa2ab11676 |
@@ -1,3 +1,81 @@
|
||||
## [23.2.0] 3/3/2025
|
||||
|
||||
### Crash
|
||||
|
||||
* Database SetMutex crash fix ([#4741](https://github.com/EQEmu/Server/pull/4741)) @Akkadius 2025-03-03
|
||||
* Fix Aura process crash with bots ([#4743](https://github.com/EQEmu/Server/pull/4743)) @Akkadius 2025-03-03
|
||||
* Fix crash in add loot code path ([#4745](https://github.com/EQEmu/Server/pull/4745)) @Akkadius 2025-03-03
|
||||
* Fix world repop crash ([#4742](https://github.com/EQEmu/Server/pull/4742)) @Akkadius 2025-03-03
|
||||
* Potential crash fix in scan close mobs ([#4744](https://github.com/EQEmu/Server/pull/4744)) @Akkadius 2025-03-03
|
||||
|
||||
### Fixes
|
||||
|
||||
* Cleanup zone buckets on instance purge. ([#4739](https://github.com/EQEmu/Server/pull/4739)) @zimp-wow 2025-03-02
|
||||
* Fix an error causing Endurance Regen to not be applied by items. ([#4738](https://github.com/EQEmu/Server/pull/4738)) @catapultam-habeo 2025-03-02
|
||||
|
||||
### World
|
||||
|
||||
* Check if port in use to avoid double booting mistakes ([#4740](https://github.com/EQEmu/Server/pull/4740)) @Akkadius 2025-03-03
|
||||
|
||||
## [23.1.0] 3/1/2025
|
||||
|
||||
### Bots
|
||||
|
||||
* Fix unresponsive bots in groups upon group wipe ([#4712](https://github.com/EQEmu/Server/pull/4712)) @nytmyr 2025-02-28
|
||||
|
||||
### Code
|
||||
|
||||
* More login <-> world code cleanup ([#4724](https://github.com/EQEmu/Server/pull/4724)) @Akkadius 2025-02-28
|
||||
|
||||
### Crash
|
||||
|
||||
* Check for directory existence before traversing in CheckForCompatibleQuestPlugins ([#4730](https://github.com/EQEmu/Server/pull/4730)) @Akkadius 2025-03-02
|
||||
* Fix filesystem crash / exception in DatabaseDumpService::RemoveCredentialsFile() ([#4731](https://github.com/EQEmu/Server/pull/4731)) @Akkadius 2025-03-01
|
||||
* Fix large file size crash in File::GetContents for windows ([#4735](https://github.com/EQEmu/Server/pull/4735)) @Akkadius 2025-03-02
|
||||
* Fix reload concurrency crash when ran from Spire ([#4733](https://github.com/EQEmu/Server/pull/4733)) @Akkadius 2025-03-02
|
||||
* Validate item in SE_SummonItemIntoBag ([#4734](https://github.com/EQEmu/Server/pull/4734)) @Akkadius 2025-03-02
|
||||
* World CLI validation ([#4728](https://github.com/EQEmu/Server/pull/4728)) @Akkadius 2025-03-01
|
||||
|
||||
### Database
|
||||
|
||||
* Remove force_interactive from big bag updates ([#4727](https://github.com/EQEmu/Server/pull/4727)) @Akkadius 2025-03-01
|
||||
|
||||
### Feature
|
||||
|
||||
* Add a rule for spells to bypass stacking rules ([#4716](https://github.com/EQEmu/Server/pull/4716)) @catapultam-habeo 2025-02-28
|
||||
* Evolving items Additions ([#4725](https://github.com/EQEmu/Server/pull/4725)) @neckkola 2025-03-01
|
||||
|
||||
### Fixes
|
||||
|
||||
* Add character_pet_name to player tables schema @Akkadius 2025-03-02
|
||||
* Add client packets to questmanager:setguild ([#4732](https://github.com/EQEmu/Server/pull/4732)) @neckkola 2025-03-01
|
||||
* Clear m_completed_shared_tasks before reloading @Akkadius 2025-02-24
|
||||
* Fix AA Reset Error Message ([#4720](https://github.com/EQEmu/Server/pull/4720)) @Kinglykrab 2025-02-28
|
||||
* Fix Issue with Suffixes/Prefixes ([#4723](https://github.com/EQEmu/Server/pull/4723)) @Kinglykrab 2025-02-28
|
||||
* Fix Trading Items to Bot Pets ([#4721](https://github.com/EQEmu/Server/pull/4721)) @MortimerGreenwald 2025-02-28
|
||||
* Refactor ApplyItemBonuses to fix double-counting of ATK and recommended levels not correctly applying ([#4713](https://github.com/EQEmu/Server/pull/4713)) @catapultam-habeo 2025-03-01
|
||||
|
||||
### Loginserver
|
||||
|
||||
* Minor cleanup ([#4729](https://github.com/EQEmu/Server/pull/4729)) @Akkadius 2025-03-01
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Key Ring Methods to Perl and Lua ([#4719](https://github.com/EQEmu/Server/pull/4719)) @Kinglykrab 2025-02-28
|
||||
* Implement eq.handin() and quest::handin() ([#4718](https://github.com/EQEmu/Server/pull/4718)) @Akkadius 2025-02-28
|
||||
|
||||
### Tasks
|
||||
|
||||
* Extend IsTaskCompleted to also be aware of shared task completion ([#4714](https://github.com/EQEmu/Server/pull/4714)) @Akkadius 2025-02-24
|
||||
|
||||
### Zone
|
||||
|
||||
* Implement Zone State Saving on Shutdown ([#4715](https://github.com/EQEmu/Server/pull/4715)) @Akkadius 2025-02-28
|
||||
|
||||
### Zone State
|
||||
|
||||
* Wrap all serialization/deserialization in try/catch ([#4726](https://github.com/EQEmu/Server/pull/4726)) @Akkadius 2025-03-01
|
||||
|
||||
## [23.0.2] 2/21/2025
|
||||
|
||||
### Bots
|
||||
|
||||
+1
-1
@@ -245,7 +245,7 @@ uint32 Database::CreateAccount(
|
||||
e.password = password;
|
||||
}
|
||||
|
||||
LogInfo("Account Attempting to be created: [{}:{}] status: {}", loginserver, name, status);
|
||||
LogInfo("Account attempting to be created loginserver [{}] name [{}] status [{}]", loginserver, name, status);
|
||||
|
||||
e = AccountRepository::InsertOne(*this, e);
|
||||
|
||||
|
||||
@@ -596,7 +596,12 @@ void DatabaseDumpService::BuildCredentialsFile()
|
||||
void DatabaseDumpService::RemoveCredentialsFile()
|
||||
{
|
||||
if (File::Exists(CREDENTIALS_FILE)) {
|
||||
std::filesystem::remove(CREDENTIALS_FILE);
|
||||
try {
|
||||
std::filesystem::remove(CREDENTIALS_FILE);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
LogError("std::filesystem::remove err [{}]", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6417,7 +6417,7 @@ ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_mo
|
||||
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
||||
)",
|
||||
.content_schema_update = false,
|
||||
.force_interactive = true
|
||||
.force_interactive = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9298,
|
||||
@@ -6481,7 +6481,7 @@ UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id`
|
||||
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
|
||||
)",
|
||||
.content_schema_update = false,
|
||||
.force_interactive = true
|
||||
.force_interactive = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9299,
|
||||
@@ -6871,6 +6871,73 @@ CREATE INDEX idx_instance_id ON data_buckets (instance_id);
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9307,
|
||||
.description = "2025_02_17_zone_state_spawns.sql",
|
||||
.check = "SHOW TABLES LIKE 'zone_state_spawns'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
CREATE TABLE `zone_state_spawns` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`zone_id` int(11) unsigned DEFAULT NULL,
|
||||
`instance_id` int(11) unsigned DEFAULT NULL,
|
||||
`is_corpse` tinyint(11) DEFAULT 0,
|
||||
`decay_in_seconds` int(11) DEFAULT 0,
|
||||
`npc_id` int(10) unsigned DEFAULT NULL,
|
||||
`spawn2_id` int(10) unsigned NOT NULL,
|
||||
`spawngroup_id` int(10) unsigned NOT NULL,
|
||||
`x` float NOT NULL,
|
||||
`y` float NOT NULL,
|
||||
`z` float NOT NULL,
|
||||
`heading` float NOT NULL,
|
||||
`respawn_time` int(10) unsigned NOT NULL,
|
||||
`variance` int(10) unsigned NOT NULL,
|
||||
`grid` int(10) unsigned DEFAULT 0,
|
||||
`current_waypoint` int(11) DEFAULT 0,
|
||||
`path_when_zone_idle` smallint(6) DEFAULT 0,
|
||||
`condition_id` smallint(5) unsigned DEFAULT 0,
|
||||
`condition_min_value` smallint(6) DEFAULT 0,
|
||||
`enabled` smallint(6) DEFAULT 1,
|
||||
`anim` smallint(5) unsigned DEFAULT 0,
|
||||
`loot_data` text DEFAULT NULL,
|
||||
`entity_variables` text DEFAULT NULL,
|
||||
`buffs` text DEFAULT NULL,
|
||||
`hp` bigint(20) DEFAULT 0,
|
||||
`mana` bigint(20) DEFAULT 0,
|
||||
`endurance` bigint(20) DEFAULT 0,
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9308,
|
||||
.description = "2025_add_multivalue_support_to_evolving_subtype.sql",
|
||||
.check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'",
|
||||
.condition = "missing",
|
||||
.match = "varchar(200)",
|
||||
.sql = R"(
|
||||
ALTER TABLE `items_evolving_details`
|
||||
CHANGE COLUMN `sub_type` `sub_type` VARCHAR(200) NULL DEFAULT '0' AFTER `type`;
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
// this one got missed being added to PEQ dumps so adding it again so it gets added when folks take a new release
|
||||
ManifestEntry{
|
||||
.version = 9309,
|
||||
.description = "2025_03_1_create_pet_names_table_if_not_exist.sql",
|
||||
.check = "SHOW TABLES LIKE 'character_pet_name'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
CREATE TABLE `character_pet_name` (
|
||||
`character_id` INT(11) NOT NULL PRIMARY KEY,
|
||||
`name` VARCHAR(64) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
)",
|
||||
},
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
@@ -562,6 +562,7 @@ void Database::PurgeExpiredInstances()
|
||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
||||
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
||||
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", imploded_instance_ids));
|
||||
}
|
||||
|
||||
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace DatabaseSchema {
|
||||
"character_pet_buffs",
|
||||
"character_pet_info",
|
||||
"character_pet_inventory",
|
||||
"character_pet_name",
|
||||
"character_peqzone_flags",
|
||||
"character_potionbelt",
|
||||
"character_skills",
|
||||
@@ -350,6 +351,7 @@ namespace DatabaseSchema {
|
||||
"shared_task_dynamic_zones",
|
||||
"shared_task_members",
|
||||
"shared_tasks",
|
||||
"zone_state_spawns",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -302,7 +302,9 @@ std::string DBcore::Escape(const std::string& s)
|
||||
|
||||
void DBcore::SetMutex(Mutex *mutex)
|
||||
{
|
||||
safe_delete(m_mutex);
|
||||
if (m_mutex && m_mutex != mutex) {
|
||||
safe_delete(m_mutex);
|
||||
}
|
||||
|
||||
DBcore::m_mutex = mutex;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
|
||||
*/
|
||||
#pragma pack(1)
|
||||
|
||||
struct LoginInfo_Struct {
|
||||
struct LoginInfo {
|
||||
/*000*/ char login_info[64];
|
||||
/*064*/ uint8 unknown064[124];
|
||||
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
||||
|
||||
@@ -149,6 +149,7 @@ namespace Logs {
|
||||
BotSpellChecks,
|
||||
BotSpellTypeChecks,
|
||||
NpcHandin,
|
||||
ZoneState,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@@ -256,7 +257,8 @@ namespace Logs {
|
||||
"Bot Settings",
|
||||
"Bot Spell Checks",
|
||||
"Bot Spell Type Checks",
|
||||
"NpcHandin"
|
||||
"NpcHandin",
|
||||
"ZoneState"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -914,6 +914,16 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogZoneState(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::General, Logs::ZoneState))\
|
||||
OutF(LogSys, Logs::General, Logs::ZoneState, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogZoneStateDetail(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::ZoneState))\
|
||||
OutF(LogSys, Logs::Detail, Logs::ZoneState, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
|
||||
+12
-13
@@ -39,6 +39,7 @@
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -90,23 +91,21 @@ std::string File::GetCwd()
|
||||
|
||||
FileContentsResult File::GetContents(const std::string &file_name)
|
||||
{
|
||||
std::string error;
|
||||
std::ifstream f;
|
||||
f.open(file_name);
|
||||
std::string line;
|
||||
std::string lines;
|
||||
if (f.is_open()) {
|
||||
while (f) {
|
||||
std::getline(f, line);
|
||||
lines += line + "\n";
|
||||
}
|
||||
std::ifstream f(file_name, std::ios::in | std::ios::binary);
|
||||
if (!f) {
|
||||
return { .error = fmt::format("Couldn't open file [{}]", file_name) };
|
||||
}
|
||||
else {
|
||||
error = fmt::format("Couldn't open file [{}]", file_name);
|
||||
|
||||
constexpr size_t CHUNK_SIZE = 4096; // Read 4KB chunks
|
||||
std::string lines;
|
||||
std::vector<char> buffer(CHUNK_SIZE);
|
||||
|
||||
while (f.read(buffer.data(), CHUNK_SIZE) || f.gcount() > 0) {
|
||||
lines.append(buffer.data(), f.gcount());
|
||||
}
|
||||
|
||||
return FileContentsResult{
|
||||
.contents = lines,
|
||||
.error = error,
|
||||
.error = {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,3 +259,76 @@ bool IpUtil::IsIPAddress(const std::string &ip_address)
|
||||
}
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h> // For inet_pton
|
||||
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h> // For inet_pton
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||
bool in_use = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
std::cerr << "WSAStartup failed\n";
|
||||
return true; // Assume in use on failure
|
||||
}
|
||||
#endif
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
return true; // Assume in use on failure
|
||||
}
|
||||
|
||||
sockaddr_in addr{};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
// Convert IP address from string to binary format
|
||||
if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0) {
|
||||
std::cerr << "Invalid IP address format: " << ip << std::endl;
|
||||
#ifdef _WIN32
|
||||
closesocket(sock);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
return true; // Assume in use on failure
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
in_use = true; // Bind failed, port is in use
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
closesocket(sock);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
|
||||
return in_use;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
int port
|
||||
);
|
||||
static bool IsIPAddress(const std::string &ip_address);
|
||||
static bool IsPortInUse(const std::string& ip, int port);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ struct LootItem {
|
||||
uint16 trivial_max_level;
|
||||
uint16 npc_min_level;
|
||||
uint16 npc_max_level;
|
||||
uint32 lootdrop_id; // required for zone state referencing
|
||||
};
|
||||
|
||||
typedef std::list<LootItem*> LootItems;
|
||||
|
||||
@@ -25,7 +25,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
||||
return;
|
||||
|
||||
if (opcode == ServerOP_UsertoWorldReq) {
|
||||
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
|
||||
auto req_in = (UsertoWorldRequest*)p.Data();
|
||||
|
||||
EQ::Net::DynamicPacket req;
|
||||
size_t i = 0;
|
||||
@@ -45,7 +45,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
||||
}
|
||||
|
||||
if (opcode == ServerOP_LSClientAuth) {
|
||||
auto req_in = (ClientAuth_Struct*)p.Data();
|
||||
auto req_in = (ClientAuth*)p.Data();
|
||||
|
||||
EQ::Net::DynamicPacket req;
|
||||
size_t i = 0;
|
||||
@@ -54,7 +54,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
||||
req.PutData(i, req_in->key, 30); i += 30;
|
||||
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
||||
req.PutUInt16(i, req_in->is_world_admin); i += 2;
|
||||
req.PutUInt32(i, req_in->ip); i += 4;
|
||||
req.PutUInt32(i, req_in->ip_address); i += 4;
|
||||
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
|
||||
@@ -76,7 +76,7 @@ void PathManager::LoadPaths()
|
||||
constexpr int path_width = 0;
|
||||
constexpr int break_length = 70;
|
||||
|
||||
std::cout << std::endl;
|
||||
LogInfo("Loading server paths");
|
||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
||||
for (const auto& [name, in_path] : paths) {
|
||||
if (!in_path.empty()) {
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
class BaseItemsEvolvingDetailsRepository {
|
||||
public:
|
||||
struct ItemsEvolvingDetails {
|
||||
uint32_t id;
|
||||
uint32_t item_evo_id;
|
||||
uint32_t item_evolve_level;
|
||||
uint32_t item_id;
|
||||
uint32_t type;
|
||||
uint32_t sub_type;
|
||||
int64_t required_amount;
|
||||
uint32_t id;
|
||||
uint32_t item_evo_id;
|
||||
uint32_t item_evolve_level;
|
||||
uint32_t item_id;
|
||||
uint32_t type;
|
||||
std::string sub_type;
|
||||
int64_t required_amount;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
e.item_evolve_level = 0;
|
||||
e.item_id = 0;
|
||||
e.type = 0;
|
||||
e.sub_type = 0;
|
||||
e.sub_type = "0";
|
||||
e.required_amount = 0;
|
||||
|
||||
return e;
|
||||
@@ -144,7 +144,7 @@ public:
|
||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.sub_type = row[5] ? row[5] : "0";
|
||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||
|
||||
return e;
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.type));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.sub_type));
|
||||
v.push_back(columns[5] + " = '" + Strings::Escape(e.sub_type) + "'");
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -211,7 +211,7 @@ public:
|
||||
v.push_back(std::to_string(e.item_evolve_level));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.sub_type));
|
||||
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||
v.push_back(std::to_string(e.required_amount));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -247,7 +247,7 @@ public:
|
||||
v.push_back(std::to_string(e.item_evolve_level));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.sub_type));
|
||||
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||
v.push_back(std::to_string(e.required_amount));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
@@ -287,7 +287,7 @@ public:
|
||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.sub_type = row[5] ? row[5] : "0";
|
||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
@@ -318,7 +318,7 @@ public:
|
||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.sub_type = row[5] ? row[5] : "0";
|
||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
@@ -399,7 +399,7 @@ public:
|
||||
v.push_back(std::to_string(e.item_evolve_level));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.sub_type));
|
||||
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||
v.push_back(std::to_string(e.required_amount));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -428,7 +428,7 @@ public:
|
||||
v.push_back(std::to_string(e.item_evolve_level));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.sub_type));
|
||||
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||
v.push_back(std::to_string(e.required_amount));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
|
||||
@@ -0,0 +1,703 @@
|
||||
/**
|
||||
* 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://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||
#define EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseZoneStateSpawnsRepository {
|
||||
public:
|
||||
struct ZoneStateSpawns {
|
||||
int64_t id;
|
||||
uint32_t zone_id;
|
||||
uint32_t instance_id;
|
||||
int8_t is_corpse;
|
||||
int32_t decay_in_seconds;
|
||||
uint32_t npc_id;
|
||||
uint32_t spawn2_id;
|
||||
uint32_t spawngroup_id;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float heading;
|
||||
uint32_t respawn_time;
|
||||
uint32_t variance;
|
||||
uint32_t grid;
|
||||
int32_t current_waypoint;
|
||||
int16_t path_when_zone_idle;
|
||||
uint16_t condition_id;
|
||||
int16_t condition_min_value;
|
||||
int16_t enabled;
|
||||
uint16_t anim;
|
||||
std::string loot_data;
|
||||
std::string entity_variables;
|
||||
std::string buffs;
|
||||
int64_t hp;
|
||||
int64_t mana;
|
||||
int64_t endurance;
|
||||
time_t created_at;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"zone_id",
|
||||
"instance_id",
|
||||
"is_corpse",
|
||||
"decay_in_seconds",
|
||||
"npc_id",
|
||||
"spawn2_id",
|
||||
"spawngroup_id",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"heading",
|
||||
"respawn_time",
|
||||
"variance",
|
||||
"grid",
|
||||
"current_waypoint",
|
||||
"path_when_zone_idle",
|
||||
"condition_id",
|
||||
"condition_min_value",
|
||||
"enabled",
|
||||
"anim",
|
||||
"loot_data",
|
||||
"entity_variables",
|
||||
"buffs",
|
||||
"hp",
|
||||
"mana",
|
||||
"endurance",
|
||||
"created_at",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"zone_id",
|
||||
"instance_id",
|
||||
"is_corpse",
|
||||
"decay_in_seconds",
|
||||
"npc_id",
|
||||
"spawn2_id",
|
||||
"spawngroup_id",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"heading",
|
||||
"respawn_time",
|
||||
"variance",
|
||||
"grid",
|
||||
"current_waypoint",
|
||||
"path_when_zone_idle",
|
||||
"condition_id",
|
||||
"condition_min_value",
|
||||
"enabled",
|
||||
"anim",
|
||||
"loot_data",
|
||||
"entity_variables",
|
||||
"buffs",
|
||||
"hp",
|
||||
"mana",
|
||||
"endurance",
|
||||
"UNIX_TIMESTAMP(created_at)",
|
||||
};
|
||||
}
|
||||
|
||||
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("zone_state_spawns");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static ZoneStateSpawns NewEntity()
|
||||
{
|
||||
ZoneStateSpawns e{};
|
||||
|
||||
e.id = 0;
|
||||
e.zone_id = 0;
|
||||
e.instance_id = 0;
|
||||
e.is_corpse = 0;
|
||||
e.decay_in_seconds = 0;
|
||||
e.npc_id = 0;
|
||||
e.spawn2_id = 0;
|
||||
e.spawngroup_id = 0;
|
||||
e.x = 0;
|
||||
e.y = 0;
|
||||
e.z = 0;
|
||||
e.heading = 0;
|
||||
e.respawn_time = 0;
|
||||
e.variance = 0;
|
||||
e.grid = 0;
|
||||
e.current_waypoint = 0;
|
||||
e.path_when_zone_idle = 0;
|
||||
e.condition_id = 0;
|
||||
e.condition_min_value = 0;
|
||||
e.enabled = 1;
|
||||
e.anim = 0;
|
||||
e.loot_data = "";
|
||||
e.entity_variables = "";
|
||||
e.buffs = "";
|
||||
e.hp = 0;
|
||||
e.mana = 0;
|
||||
e.endurance = 0;
|
||||
e.created_at = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static ZoneStateSpawns GetZoneStateSpawns(
|
||||
const std::vector<ZoneStateSpawns> &zone_state_spawnss,
|
||||
int zone_state_spawns_id
|
||||
)
|
||||
{
|
||||
for (auto &zone_state_spawns : zone_state_spawnss) {
|
||||
if (zone_state_spawns.id == zone_state_spawns_id) {
|
||||
return zone_state_spawns;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static ZoneStateSpawns FindOne(
|
||||
Database& db,
|
||||
int zone_state_spawns_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
zone_state_spawns_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
ZoneStateSpawns e{};
|
||||
|
||||
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||
e.decay_in_seconds = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.npc_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.spawn2_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.spawngroup_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.x = row[8] ? strtof(row[8], nullptr) : 0;
|
||||
e.y = row[9] ? strtof(row[9], nullptr) : 0;
|
||||
e.z = row[10] ? strtof(row[10], nullptr) : 0;
|
||||
e.heading = row[11] ? strtof(row[11], nullptr) : 0;
|
||||
e.respawn_time = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.variance = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.grid = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.current_waypoint = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.path_when_zone_idle = row[16] ? static_cast<int16_t>(atoi(row[16])) : 0;
|
||||
e.condition_id = row[17] ? static_cast<uint16_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.condition_min_value = row[18] ? static_cast<int16_t>(atoi(row[18])) : 0;
|
||||
e.enabled = row[19] ? static_cast<int16_t>(atoi(row[19])) : 1;
|
||||
e.anim = row[20] ? static_cast<uint16_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.loot_data = row[21] ? row[21] : "";
|
||||
e.entity_variables = row[22] ? row[22] : "";
|
||||
e.buffs = row[23] ? row[23] : "";
|
||||
e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0;
|
||||
e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||
e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||
e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int zone_state_spawns_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
zone_state_spawns_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const ZoneStateSpawns &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.zone_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.instance_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.is_corpse));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.decay_in_seconds));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.npc_id));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.spawn2_id));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.spawngroup_id));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.x));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.y));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.z));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.heading));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.respawn_time));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.variance));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.grid));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.current_waypoint));
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.path_when_zone_idle));
|
||||
v.push_back(columns[17] + " = " + std::to_string(e.condition_id));
|
||||
v.push_back(columns[18] + " = " + std::to_string(e.condition_min_value));
|
||||
v.push_back(columns[19] + " = " + std::to_string(e.enabled));
|
||||
v.push_back(columns[20] + " = " + std::to_string(e.anim));
|
||||
v.push_back(columns[21] + " = '" + Strings::Escape(e.loot_data) + "'");
|
||||
v.push_back(columns[22] + " = '" + Strings::Escape(e.entity_variables) + "'");
|
||||
v.push_back(columns[23] + " = '" + Strings::Escape(e.buffs) + "'");
|
||||
v.push_back(columns[24] + " = " + std::to_string(e.hp));
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.mana));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.endurance));
|
||||
v.push_back(columns[27] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static ZoneStateSpawns InsertOne(
|
||||
Database& db,
|
||||
ZoneStateSpawns e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.is_corpse));
|
||||
v.push_back(std::to_string(e.decay_in_seconds));
|
||||
v.push_back(std::to_string(e.npc_id));
|
||||
v.push_back(std::to_string(e.spawn2_id));
|
||||
v.push_back(std::to_string(e.spawngroup_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.respawn_time));
|
||||
v.push_back(std::to_string(e.variance));
|
||||
v.push_back(std::to_string(e.grid));
|
||||
v.push_back(std::to_string(e.current_waypoint));
|
||||
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||
v.push_back(std::to_string(e.condition_id));
|
||||
v.push_back(std::to_string(e.condition_min_value));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
v.push_back(std::to_string(e.anim));
|
||||
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||
v.push_back(std::to_string(e.hp));
|
||||
v.push_back(std::to_string(e.mana));
|
||||
v.push_back(std::to_string(e.endurance));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
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<ZoneStateSpawns> &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.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.is_corpse));
|
||||
v.push_back(std::to_string(e.decay_in_seconds));
|
||||
v.push_back(std::to_string(e.npc_id));
|
||||
v.push_back(std::to_string(e.spawn2_id));
|
||||
v.push_back(std::to_string(e.spawngroup_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.respawn_time));
|
||||
v.push_back(std::to_string(e.variance));
|
||||
v.push_back(std::to_string(e.grid));
|
||||
v.push_back(std::to_string(e.current_waypoint));
|
||||
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||
v.push_back(std::to_string(e.condition_id));
|
||||
v.push_back(std::to_string(e.condition_min_value));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
v.push_back(std::to_string(e.anim));
|
||||
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||
v.push_back(std::to_string(e.hp));
|
||||
v.push_back(std::to_string(e.mana));
|
||||
v.push_back(std::to_string(e.endurance));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
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<ZoneStateSpawns> All(Database& db)
|
||||
{
|
||||
std::vector<ZoneStateSpawns> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
ZoneStateSpawns e{};
|
||||
|
||||
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||
e.decay_in_seconds = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.npc_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.spawn2_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.spawngroup_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.x = row[8] ? strtof(row[8], nullptr) : 0;
|
||||
e.y = row[9] ? strtof(row[9], nullptr) : 0;
|
||||
e.z = row[10] ? strtof(row[10], nullptr) : 0;
|
||||
e.heading = row[11] ? strtof(row[11], nullptr) : 0;
|
||||
e.respawn_time = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.variance = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.grid = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.current_waypoint = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.path_when_zone_idle = row[16] ? static_cast<int16_t>(atoi(row[16])) : 0;
|
||||
e.condition_id = row[17] ? static_cast<uint16_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.condition_min_value = row[18] ? static_cast<int16_t>(atoi(row[18])) : 0;
|
||||
e.enabled = row[19] ? static_cast<int16_t>(atoi(row[19])) : 1;
|
||||
e.anim = row[20] ? static_cast<uint16_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.loot_data = row[21] ? row[21] : "";
|
||||
e.entity_variables = row[22] ? row[22] : "";
|
||||
e.buffs = row[23] ? row[23] : "";
|
||||
e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0;
|
||||
e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||
e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||
e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<ZoneStateSpawns> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<ZoneStateSpawns> 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) {
|
||||
ZoneStateSpawns e{};
|
||||
|
||||
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||
e.decay_in_seconds = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.npc_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.spawn2_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.spawngroup_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.x = row[8] ? strtof(row[8], nullptr) : 0;
|
||||
e.y = row[9] ? strtof(row[9], nullptr) : 0;
|
||||
e.z = row[10] ? strtof(row[10], nullptr) : 0;
|
||||
e.heading = row[11] ? strtof(row[11], nullptr) : 0;
|
||||
e.respawn_time = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.variance = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.grid = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.current_waypoint = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.path_when_zone_idle = row[16] ? static_cast<int16_t>(atoi(row[16])) : 0;
|
||||
e.condition_id = row[17] ? static_cast<uint16_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.condition_min_value = row[18] ? static_cast<int16_t>(atoi(row[18])) : 0;
|
||||
e.enabled = row[19] ? static_cast<int16_t>(atoi(row[19])) : 1;
|
||||
e.anim = row[20] ? static_cast<uint16_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.loot_data = row[21] ? row[21] : "";
|
||||
e.entity_variables = row[22] ? row[22] : "";
|
||||
e.buffs = row[23] ? row[23] : "";
|
||||
e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0;
|
||||
e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||
e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||
e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const ZoneStateSpawns &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.is_corpse));
|
||||
v.push_back(std::to_string(e.decay_in_seconds));
|
||||
v.push_back(std::to_string(e.npc_id));
|
||||
v.push_back(std::to_string(e.spawn2_id));
|
||||
v.push_back(std::to_string(e.spawngroup_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.respawn_time));
|
||||
v.push_back(std::to_string(e.variance));
|
||||
v.push_back(std::to_string(e.grid));
|
||||
v.push_back(std::to_string(e.current_waypoint));
|
||||
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||
v.push_back(std::to_string(e.condition_id));
|
||||
v.push_back(std::to_string(e.condition_min_value));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
v.push_back(std::to_string(e.anim));
|
||||
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||
v.push_back(std::to_string(e.hp));
|
||||
v.push_back(std::to_string(e.mana));
|
||||
v.push_back(std::to_string(e.endurance));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<ZoneStateSpawns> &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.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.is_corpse));
|
||||
v.push_back(std::to_string(e.decay_in_seconds));
|
||||
v.push_back(std::to_string(e.npc_id));
|
||||
v.push_back(std::to_string(e.spawn2_id));
|
||||
v.push_back(std::to_string(e.spawngroup_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.respawn_time));
|
||||
v.push_back(std::to_string(e.variance));
|
||||
v.push_back(std::to_string(e.grid));
|
||||
v.push_back(std::to_string(e.current_waypoint));
|
||||
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||
v.push_back(std::to_string(e.condition_id));
|
||||
v.push_back(std::to_string(e.condition_min_value));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
v.push_back(std::to_string(e.anim));
|
||||
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||
v.push_back(std::to_string(e.hp));
|
||||
v.push_back(std::to_string(e.mana));
|
||||
v.push_back(std::to_string(e.endurance));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||
#define EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_zone_state_spawns_repository.h"
|
||||
|
||||
class ZoneStateSpawnsRepository: public BaseZoneStateSpawnsRepository {
|
||||
public:
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||
@@ -374,6 +374,7 @@ RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross z
|
||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
|
||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
|
||||
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
||||
RULE_BOOL(Zone, StateSavingOnShutdown, true, "Set to true if you want zones to save state on shutdown (npcs, corpses, loot, entity variables, buffs etc.)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Map)
|
||||
@@ -528,6 +529,7 @@ RULE_INT(Spells, TargetedAOEMaxTargets, 4, "Max number of targets a Targeted AOE
|
||||
RULE_INT(Spells, PointBlankAOEMaxTargets, 0, "Max number of targets a Point-Blank AOE spell can cast on. Set to 0 for no limit.")
|
||||
RULE_INT(Spells, DefaultAOEMaxTargets, 0, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
|
||||
RULE_BOOL(Spells, AllowFocusOnSkillDamageSpells, false, "Allow focus effects 185, 459, and 482 to enhance SkillAttack spell effect 193")
|
||||
RULE_STRING(Spells, AlwaysStackSpells, "", "Comma-Seperated list of spell IDs to always stack with every other spell, except themselves.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@@ -1145,6 +1147,7 @@ RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell
|
||||
RULE_BOOL(Items, SummonItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots of items in Client::SummonItem")
|
||||
RULE_BOOL(Items, AugmentItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots by players")
|
||||
RULE_BOOL(Items, AlwaysReturnHandins, true, "Enable this to always return handins to the player")
|
||||
RULE_BOOL(Items, NPCUseRecommendedLevels, false, "Enable to have NPCs scale item stats by recommended levels")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Parcel)
|
||||
|
||||
+60
-43
@@ -677,36 +677,53 @@ struct ServerLSPlayerZoneChange_Struct {
|
||||
uint32 to; // 0 = world
|
||||
};
|
||||
|
||||
struct ClientAuth_Struct {
|
||||
struct ClientAuth {
|
||||
uint32 loginserver_account_id; // ID# in login server's db
|
||||
char loginserver_name[64];
|
||||
char account_name[30]; // username in login server's db
|
||||
char key[30]; // the Key the client will present
|
||||
uint8 lsadmin; // login server admin level
|
||||
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||
uint32 ip;
|
||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
||||
char loginserver_name[64];
|
||||
char account_name[30]; // username in login server's db
|
||||
char key[30]; // the key the client will present
|
||||
uint8 lsadmin; // login server admin level
|
||||
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||
uint32 ip_address;
|
||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
||||
|
||||
template <class Archive>
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(loginserver_account_id, loginserver_name, account_name, key, lsadmin, is_world_admin, ip, is_client_from_local_network);
|
||||
ar(
|
||||
loginserver_account_id,
|
||||
loginserver_name,
|
||||
account_name,
|
||||
key,
|
||||
lsadmin,
|
||||
is_world_admin,
|
||||
ip_address,
|
||||
is_client_from_local_network
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct ClientAuthLegacy_Struct {
|
||||
struct ClientAuthLegacy {
|
||||
uint32 loginserver_account_id; // ID# in login server's db
|
||||
char loginserver_account_name[30]; // username in login server's db
|
||||
char key[30]; // the Key the client will present
|
||||
uint8 loginserver_admin_level; // login server admin level
|
||||
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||
uint32 ip;
|
||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
||||
char loginserver_account_name[30]; // username in login server's db
|
||||
char key[30]; // the key the client will present
|
||||
uint8 loginserver_admin_level; // login server admin level
|
||||
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||
uint32 ip_address;
|
||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
||||
|
||||
template <class Archive>
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(loginserver_account_id, loginserver_account_name, key, loginserver_admin_level, is_world_admin, ip, is_client_from_local_network);
|
||||
ar(
|
||||
loginserver_account_id,
|
||||
loginserver_account_name,
|
||||
key,
|
||||
loginserver_admin_level,
|
||||
is_world_admin,
|
||||
ip_address,
|
||||
is_client_from_local_network
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -834,38 +851,38 @@ struct ServerSyncWorldList_Struct {
|
||||
bool placeholder;
|
||||
};
|
||||
|
||||
struct UsertoWorldRequestLegacy_Struct {
|
||||
uint32 lsaccountid;
|
||||
uint32 worldid;
|
||||
uint32 FromID;
|
||||
uint32 ToID;
|
||||
char IPAddr[64];
|
||||
};
|
||||
|
||||
struct UsertoWorldRequest_Struct {
|
||||
uint32 lsaccountid;
|
||||
uint32 worldid;
|
||||
uint32 FromID;
|
||||
uint32 ToID;
|
||||
char IPAddr[64];
|
||||
char login[64];
|
||||
};
|
||||
|
||||
struct UsertoWorldResponseLegacy_Struct {
|
||||
struct UsertoWorldRequestLegacy {
|
||||
uint32 lsaccountid;
|
||||
uint32 worldid;
|
||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||
uint32 FromID;
|
||||
uint32 ToID;
|
||||
char IPAddr[64];
|
||||
};
|
||||
|
||||
struct UsertoWorldRequest {
|
||||
uint32 lsaccountid;
|
||||
uint32 worldid;
|
||||
uint32 FromID; // appears to be unused today
|
||||
uint32 ToID; // appears to be unused today
|
||||
char IPAddr[64];
|
||||
char login[64];
|
||||
};
|
||||
|
||||
struct UsertoWorldResponseLegacy {
|
||||
uint32 lsaccountid;
|
||||
uint32 worldid;
|
||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||
uint32 FromID;
|
||||
uint32 ToID;
|
||||
};
|
||||
|
||||
struct UsertoWorldResponse_Struct {
|
||||
struct UsertoWorldResponse {
|
||||
uint32 lsaccountid;
|
||||
uint32 worldid;
|
||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||
uint32 FromID;
|
||||
uint32 ToID;
|
||||
char login[64];
|
||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||
uint32 FromID; // appears to be unused today
|
||||
uint32 ToID; // appears to be unused today
|
||||
char login[64];
|
||||
};
|
||||
|
||||
// generic struct to be used for alot of simple zone->world questions
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "23.0.2-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "23.2.0-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,7 +42,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9306
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9309
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||
|
||||
#endif
|
||||
|
||||
@@ -143,7 +143,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
||||
packet.ToString()
|
||||
);
|
||||
|
||||
if (packet.Length() < sizeof(UsertoWorldResponseLegacy_Struct)) {
|
||||
if (packet.Length() < sizeof(UsertoWorldResponseLegacy)) {
|
||||
LogError(
|
||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||
"but was too small. Discarded to avoid buffer overrun"
|
||||
@@ -152,7 +152,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
||||
return;
|
||||
}
|
||||
|
||||
auto *res = (UsertoWorldResponseLegacy_Struct *) packet.Data();
|
||||
auto *res = (UsertoWorldResponseLegacy *) packet.Data();
|
||||
|
||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||
Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu");
|
||||
@@ -229,7 +229,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
||||
packet.ToString()
|
||||
);
|
||||
|
||||
if (packet.Length() < sizeof(UsertoWorldResponse_Struct)) {
|
||||
if (packet.Length() < sizeof(UsertoWorldResponse)) {
|
||||
LogError(
|
||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||
"but was too small. Discarded to avoid buffer overrun"
|
||||
@@ -238,7 +238,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = (UsertoWorldResponse_Struct *) packet.Data();
|
||||
auto res = (UsertoWorldResponse *) packet.Data();
|
||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||
|
||||
Client *c = server.client_manager->GetClient(
|
||||
@@ -358,14 +358,12 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet
|
||||
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
||||
{
|
||||
if (m_is_server_logged_in) {
|
||||
LogError(
|
||||
"Login server was already marked as logged in, aborting"
|
||||
);
|
||||
LogInfo("Login server was already marked as logged in, returning");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HandleNewWorldserverValidation(req)) {
|
||||
LogError("WorldServer::HandleNewWorldserver failed validation rules");
|
||||
LogError("failed validation rules");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -494,7 +492,7 @@ void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u)
|
||||
void WorldServer::SendClientAuthToWorld(Client *c)
|
||||
{
|
||||
EQ::Net::DynamicPacket outapp;
|
||||
ClientAuth_Struct a{};
|
||||
ClientAuth a{};
|
||||
|
||||
a.loginserver_account_id = c->GetAccountID();
|
||||
|
||||
@@ -503,7 +501,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
||||
|
||||
a.lsadmin = 0;
|
||||
a.is_world_admin = 0;
|
||||
a.ip = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
||||
a.ip_address = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
||||
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
||||
|
||||
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
||||
@@ -521,7 +519,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
||||
}
|
||||
|
||||
struct in_addr ip_addr{};
|
||||
ip_addr.s_addr = a.ip;
|
||||
ip_addr.s_addr = a.ip_address;
|
||||
|
||||
LogInfo(
|
||||
"Client authentication response: world_address [{}] client_address [{}]",
|
||||
|
||||
@@ -80,7 +80,7 @@ private:
|
||||
std::string m_server_version;
|
||||
bool m_is_server_authorized_to_list;
|
||||
bool m_is_server_logged_in;
|
||||
bool m_is_server_trusted;
|
||||
bool m_is_server_trusted; // this is primarily for worldserver being able to push updates to the loginserver
|
||||
|
||||
static void FormatWorldServerName(char *name, int8 server_list_type);
|
||||
};
|
||||
|
||||
@@ -154,9 +154,9 @@ void WorldServerManager::SendUserLoginToWorldRequest(
|
||||
|
||||
if (iter != m_world_servers.end()) {
|
||||
EQ::Net::DynamicPacket outapp;
|
||||
outapp.Resize(sizeof(UsertoWorldRequest_Struct));
|
||||
outapp.Resize(sizeof(UsertoWorldRequest));
|
||||
|
||||
auto *r = reinterpret_cast<UsertoWorldRequest_Struct *>(outapp.Data());
|
||||
auto *r = reinterpret_cast<UsertoWorldRequest *>(outapp.Data());
|
||||
r->worldid = server_id;
|
||||
r->lsaccountid = client_account_id;
|
||||
strncpy(r->login, client_loginserver.c_str(), 64);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "23.0.2",
|
||||
"version": "23.2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
+14
-14
@@ -447,30 +447,30 @@ void Client::SendPostEnterWorld() {
|
||||
|
||||
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size != sizeof(LoginInfo_Struct)) {
|
||||
if (app->size != sizeof(LoginInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *login_info = (LoginInfo_Struct *) app->pBuffer;
|
||||
auto *r = (LoginInfo *) app->pBuffer;
|
||||
|
||||
// Quagmire - max len for name is 18, pass 15
|
||||
char name[19] = {0};
|
||||
char password[16] = {0};
|
||||
strn0cpy(name, (char *) login_info->login_info, 18);
|
||||
strn0cpy(password, (char *) &(login_info->login_info[strlen(name) + 1]), 15);
|
||||
strn0cpy(name, (char *) r->login_info, 18);
|
||||
strn0cpy(password, (char *) &(r->login_info[strlen(name) + 1]), 15);
|
||||
|
||||
LogDebug("Receiving Login Info Packet from Client | name [{0}] password [{1}]", name, password);
|
||||
LogDebug("Receiving login info packet from client | name [{}] password [{}]", name, password);
|
||||
|
||||
if (strlen(password) <= 1) {
|
||||
LogInfo("Login without a password");
|
||||
return false;
|
||||
}
|
||||
|
||||
is_player_zoning = (login_info->zoning == 1);
|
||||
is_player_zoning = (r->zoning == 1);
|
||||
|
||||
uint32 id = Strings::ToInt(name);
|
||||
if (id == 0) {
|
||||
LogWarning("Receiving Login Info Packet from Client | account_id is 0 - disconnecting");
|
||||
LogWarning("Receiving login info packet from client | account_id is 0 - disconnecting");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -480,15 +480,15 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||
LogClientLogin("Checking authentication id [{}] passed", id);
|
||||
if (!is_player_zoning) {
|
||||
// Track who is in and who is out of the game
|
||||
char *inout= (char *) "";
|
||||
std::string in_out;
|
||||
|
||||
if (cle->GetOnline() == CLE_Status::Never){
|
||||
if (cle->GetOnline() == CLE_Status::Never) {
|
||||
// Desktop -> Char Select
|
||||
inout = (char *) "In";
|
||||
in_out = "in";
|
||||
}
|
||||
else {
|
||||
// Game -> Char Select
|
||||
inout=(char *) "Out";
|
||||
in_out = "out";
|
||||
}
|
||||
|
||||
// Always at Char select at this point.
|
||||
@@ -497,7 +497,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||
// Could use a Logging Out Completely message somewhere.
|
||||
cle->SetOnline(CLE_Status::CharSelect);
|
||||
|
||||
LogInfo("Account ({}) Logging({}) to character select :: LSID [{}] ", cle->AccountName(), inout, cle->LSID());
|
||||
LogInfo("Account ({}) Logging ({}) to character select :: LSID [{}] ", cle->AccountName(), in_out, cle->LSID());
|
||||
}
|
||||
else {
|
||||
cle->SetOnline();
|
||||
@@ -545,7 +545,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||
if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) {
|
||||
// Modified clients can utilize this unused block in login_info to send custom payloads on login
|
||||
// which indicates they are using custom client files with the correct version, based on key payload.
|
||||
const auto client_key = std::string(reinterpret_cast<char*>(login_info->unknown064));
|
||||
const auto client_key = std::string(reinterpret_cast<char*>(r->unknown064));
|
||||
if (custom_files_key != client_key) {
|
||||
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
||||
SendUnsupportedClientPacket(message);
|
||||
@@ -1434,7 +1434,7 @@ void Client::EnterWorld(bool TryBootup) {
|
||||
}
|
||||
else {
|
||||
if (TryBootup) {
|
||||
LogInfo("Attempting autobootup of [{}] ([{}]:[{}])", zone_name, zone_id, instance_id);
|
||||
LogInfo("Attempting autobootup of [{}] [{}] [{}]", zone_name, zone_id, instance_id);
|
||||
autobootup_timeout.Start();
|
||||
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
||||
if (zone_waiting_for_bootup == 0) {
|
||||
|
||||
+159
-182
@@ -1,20 +1,3 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2005 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "../common/global_define.h"
|
||||
#include "cliententry.h"
|
||||
#include "clientlist.h"
|
||||
@@ -24,93 +7,86 @@
|
||||
#include "worlddb.h"
|
||||
#include "zoneserver.h"
|
||||
#include "world_config.h"
|
||||
#include "../common/guilds.h"
|
||||
#include "../common/strings.h"
|
||||
|
||||
extern uint32 numplayers;
|
||||
extern LoginServerList loginserverlist;
|
||||
extern ClientList client_list;
|
||||
extern volatile bool RunLoops;
|
||||
extern uint32 numplayers;
|
||||
extern LoginServerList loginserverlist;
|
||||
extern ClientList client_list;
|
||||
extern volatile bool RunLoops;
|
||||
extern SharedTaskManager shared_task_manager;
|
||||
|
||||
/**
|
||||
* @param in_id
|
||||
* @param in_loginserver_id
|
||||
* @param in_loginserver_name
|
||||
* @param in_login_name
|
||||
* @param in_login_key
|
||||
* @param in_is_world_admin
|
||||
* @param ip
|
||||
* @param local
|
||||
*/
|
||||
ClientListEntry::ClientListEntry(
|
||||
uint32 in_id,
|
||||
uint32 in_loginserver_id,
|
||||
const char *in_loginserver_name,
|
||||
const char *in_login_name,
|
||||
const char *in_login_key,
|
||||
int16 in_is_world_admin,
|
||||
uint32 ip,
|
||||
uint32 id,
|
||||
uint32 login_server_id,
|
||||
const char *login_server_name,
|
||||
const char *account_name,
|
||||
const char *login_key,
|
||||
int16 is_world_admin,
|
||||
uint32 ip_address,
|
||||
uint8 local
|
||||
)
|
||||
: id(in_id)
|
||||
: m_id(id)
|
||||
{
|
||||
ClearVars(true);
|
||||
|
||||
LogDebug(
|
||||
"in_id [{0}] in_loginserver_id [{1}] in_loginserver_name [{2}] in_login_name [{3}] in_login_key [{4}] "
|
||||
" in_is_world_admin [{5}] ip [{6}] local [{7}]",
|
||||
in_id,
|
||||
in_loginserver_id,
|
||||
in_loginserver_name,
|
||||
in_login_name,
|
||||
in_login_key,
|
||||
in_is_world_admin,
|
||||
ip,
|
||||
"id [{}] loginserver_id [{}] loginserver_name [{}] login_name [{}] login_key [{}] is_world_admin [{}] ip [{}] local [{}]",
|
||||
id,
|
||||
login_server_id,
|
||||
login_server_name,
|
||||
account_name,
|
||||
login_key,
|
||||
is_world_admin,
|
||||
ip_address,
|
||||
local
|
||||
);
|
||||
|
||||
pIP = ip;
|
||||
pLSID = in_loginserver_id;
|
||||
if (in_loginserver_id > 0) {
|
||||
paccountid = database.GetAccountIDFromLSID(in_loginserver_name, in_loginserver_id, paccountname, &padmin);
|
||||
m_ip_address = ip_address;
|
||||
m_login_server_id = login_server_id;
|
||||
if (login_server_id > 0) {
|
||||
m_account_id = database.GetAccountIDFromLSID(
|
||||
login_server_name,
|
||||
login_server_id,
|
||||
m_account_name,
|
||||
&m_admin
|
||||
);
|
||||
}
|
||||
|
||||
strn0cpy(loginserver_account_name, in_login_name, sizeof(loginserver_account_name));
|
||||
strn0cpy(plskey, in_login_key, sizeof(plskey));
|
||||
strn0cpy(source_loginserver, in_loginserver_name, sizeof(source_loginserver));
|
||||
pworldadmin = in_is_world_admin;
|
||||
plocal = (local == 1);
|
||||
strn0cpy(m_login_account_name, account_name, sizeof(m_login_account_name));
|
||||
strn0cpy(m_key, login_key, sizeof(m_key));
|
||||
strn0cpy(m_source_loginserver, login_server_name, sizeof(m_source_loginserver));
|
||||
|
||||
memset(pLFGComments, 0, 64);
|
||||
m_world_admin = is_world_admin;
|
||||
m_is_local = (local == 1);
|
||||
|
||||
memset(m_lfg_comments, 0, 64);
|
||||
}
|
||||
|
||||
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
||||
: id(in_id)
|
||||
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online)
|
||||
: m_id(in_id)
|
||||
{
|
||||
ClearVars(true);
|
||||
|
||||
pIP = 0;
|
||||
pLSID = scl->LSAccountID;
|
||||
strn0cpy(loginserver_account_name, scl->name, sizeof(loginserver_account_name));
|
||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
||||
pworldadmin = 0;
|
||||
m_ip_address = 0;
|
||||
m_login_server_id = scl->LSAccountID;
|
||||
strn0cpy(m_login_account_name, scl->name, sizeof(m_login_account_name));
|
||||
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||
m_world_admin = 0;
|
||||
|
||||
paccountid = scl->AccountID;
|
||||
strn0cpy(paccountname, scl->AccountName, sizeof(paccountname));
|
||||
padmin = scl->Admin;
|
||||
m_account_id = scl->AccountID;
|
||||
strn0cpy(m_account_name, scl->AccountName, sizeof(m_account_name));
|
||||
m_admin = scl->Admin;
|
||||
|
||||
pinstance = 0;
|
||||
pLFGFromLevel = 0;
|
||||
pLFGToLevel = 0;
|
||||
pLFGMatchFilter = false;
|
||||
memset(pLFGComments, 0, 64);
|
||||
m_instance = 0;
|
||||
m_lfg_from_level = 0;
|
||||
m_lfg_to_level = 0;
|
||||
m_lfg_match_filter = false;
|
||||
memset(m_lfg_comments, 0, 64);
|
||||
|
||||
if (iOnline >= CLE_Status::Zoning) {
|
||||
Update(iZS, scl, iOnline);
|
||||
if (online >= CLE_Status::Zoning) {
|
||||
Update(z, scl, online);
|
||||
}
|
||||
else {
|
||||
SetOnline(iOnline);
|
||||
SetOnline(online);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,16 +96,16 @@ ClientListEntry::~ClientListEntry()
|
||||
Camp(); // updates zoneserver's numplayers
|
||||
client_list.RemoveCLEReferances(this);
|
||||
}
|
||||
for (auto& elem : tell_queue) {
|
||||
for (auto &elem: m_tell_queue) {
|
||||
safe_delete_array(elem);
|
||||
}
|
||||
tell_queue.clear();
|
||||
m_tell_queue.clear();
|
||||
}
|
||||
|
||||
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
||||
{
|
||||
pcharid = iCharID;
|
||||
strn0cpy(pname, iCharName, sizeof(pname));
|
||||
m_char_id = iCharID;
|
||||
strn0cpy(m_char_name, iCharName, sizeof(m_char_name));
|
||||
}
|
||||
|
||||
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
||||
@@ -142,20 +118,20 @@ void ClientListEntry::SetOnline(CLE_Status iOnline)
|
||||
static_cast<int>(iOnline)
|
||||
);
|
||||
|
||||
if (iOnline >= CLE_Status::Online && pOnline < CLE_Status::Online) {
|
||||
if (iOnline >= CLE_Status::Online && m_online < CLE_Status::Online) {
|
||||
numplayers++;
|
||||
}
|
||||
else if (iOnline < CLE_Status::Online && pOnline >= CLE_Status::Online) {
|
||||
else if (iOnline < CLE_Status::Online && m_online >= CLE_Status::Online) {
|
||||
numplayers--;
|
||||
}
|
||||
if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) {
|
||||
pOnline = iOnline;
|
||||
if (iOnline != CLE_Status::Online || m_online < CLE_Status::Online) {
|
||||
m_online = iOnline;
|
||||
}
|
||||
if (iOnline < CLE_Status::Zoning) {
|
||||
Camp();
|
||||
}
|
||||
if (pOnline >= CLE_Status::Online) {
|
||||
stale = 0;
|
||||
if (m_online >= CLE_Status::Online) {
|
||||
m_stale = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,49 +169,49 @@ void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz)
|
||||
|
||||
void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
||||
{
|
||||
if (pzoneserver != iZS) {
|
||||
if (pzoneserver) {
|
||||
pzoneserver->RemovePlayer();
|
||||
LSUpdate(pzoneserver);
|
||||
if (m_zone_server != iZS) {
|
||||
if (m_zone_server) {
|
||||
m_zone_server->RemovePlayer();
|
||||
LSUpdate(m_zone_server);
|
||||
}
|
||||
if (iZS) {
|
||||
iZS->AddPlayer();
|
||||
LSUpdate(iZS);
|
||||
}
|
||||
}
|
||||
pzoneserver = iZS;
|
||||
pzone = scl->zone;
|
||||
pinstance = scl->instance_id;
|
||||
pcharid = scl->charid;
|
||||
m_zone_server = iZS;
|
||||
m_zone = scl->zone;
|
||||
m_instance = scl->instance_id;
|
||||
m_char_id = scl->charid;
|
||||
|
||||
strcpy(pname, scl->name);
|
||||
if (paccountid == 0) {
|
||||
paccountid = scl->AccountID;
|
||||
strcpy(paccountname, scl->AccountName);
|
||||
strcpy(loginserver_account_name, scl->AccountName);
|
||||
pIP = scl->IP;
|
||||
pLSID = scl->LSAccountID;
|
||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
||||
strcpy(m_char_name, scl->name);
|
||||
if (m_account_id == 0) {
|
||||
m_account_id = scl->AccountID;
|
||||
strcpy(m_account_name, scl->AccountName);
|
||||
strcpy(m_login_account_name, scl->AccountName);
|
||||
m_ip_address = scl->IP;
|
||||
m_login_server_id = scl->LSAccountID;
|
||||
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||
}
|
||||
padmin = scl->Admin;
|
||||
plevel = scl->level;
|
||||
pclass_ = scl->class_;
|
||||
prace = scl->race;
|
||||
panon = scl->anon;
|
||||
ptellsoff = scl->tellsoff;
|
||||
pguild_id = scl->guild_id;
|
||||
pguild_rank = scl->guild_rank;
|
||||
pguild_tribute_opt_in = scl->guild_tribute_opt_in;
|
||||
pLFG = scl->LFG;
|
||||
gm = scl->gm;
|
||||
pClientVersion = scl->ClientVersion;
|
||||
m_admin = scl->Admin;
|
||||
m_level = scl->level;
|
||||
m_class_ = scl->class_;
|
||||
m_race = scl->race;
|
||||
m_anon = scl->anon;
|
||||
m_tells_off = scl->tellsoff;
|
||||
m_guild_id = scl->guild_id;
|
||||
m_guild_rank = scl->guild_rank;
|
||||
m_guild_tribute_opt_in = scl->guild_tribute_opt_in;
|
||||
m_lfg = scl->LFG;
|
||||
m_gm = scl->gm;
|
||||
m_client_version = scl->ClientVersion;
|
||||
|
||||
// Fields from the LFG Window
|
||||
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
||||
pLFGFromLevel = scl->LFGFromLevel;
|
||||
pLFGToLevel = scl->LFGToLevel;
|
||||
pLFGMatchFilter = scl->LFGMatchFilter;
|
||||
memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments));
|
||||
m_lfg_from_level = scl->LFGFromLevel;
|
||||
m_lfg_to_level = scl->LFGToLevel;
|
||||
m_lfg_match_filter = scl->LFGMatchFilter;
|
||||
memcpy(m_lfg_comments, scl->LFGComments, sizeof(m_lfg_comments));
|
||||
}
|
||||
|
||||
SetOnline(iOnline);
|
||||
@@ -243,76 +219,76 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_
|
||||
|
||||
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
||||
{
|
||||
if (iZS != 0 && iZS != pzoneserver) {
|
||||
if (iZS != 0 && iZS != m_zone_server) {
|
||||
return;
|
||||
}
|
||||
SetOnline(iOnline);
|
||||
|
||||
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
||||
|
||||
if (pzoneserver) {
|
||||
pzoneserver->RemovePlayer();
|
||||
LSUpdate(pzoneserver);
|
||||
if (m_zone_server) {
|
||||
m_zone_server->RemovePlayer();
|
||||
LSUpdate(m_zone_server);
|
||||
}
|
||||
pzoneserver = 0;
|
||||
pzone = 0;
|
||||
m_zone_server = 0;
|
||||
m_zone = 0;
|
||||
}
|
||||
|
||||
void ClientListEntry::ClearVars(bool iAll)
|
||||
{
|
||||
if (iAll) {
|
||||
pOnline = CLE_Status::Never;
|
||||
stale = 0;
|
||||
m_online = CLE_Status::Never;
|
||||
m_stale = 0;
|
||||
|
||||
pLSID = 0;
|
||||
memset(loginserver_account_name, 0, sizeof(loginserver_account_name));
|
||||
memset(plskey, 0, sizeof(plskey));
|
||||
pworldadmin = 0;
|
||||
m_login_server_id = 0;
|
||||
memset(m_login_account_name, 0, sizeof(m_login_account_name));
|
||||
memset(m_key, 0, sizeof(m_key));
|
||||
m_world_admin = 0;
|
||||
|
||||
paccountid = 0;
|
||||
memset(paccountname, 0, sizeof(paccountname));
|
||||
padmin = AccountStatus::Player;
|
||||
m_account_id = 0;
|
||||
memset(m_account_name, 0, sizeof(m_account_name));
|
||||
m_admin = AccountStatus::Player;
|
||||
}
|
||||
pzoneserver = 0;
|
||||
pzone = 0;
|
||||
pcharid = 0;
|
||||
memset(pname, 0, sizeof(pname));
|
||||
plevel = 0;
|
||||
pclass_ = 0;
|
||||
prace = 0;
|
||||
panon = 0;
|
||||
ptellsoff = 0;
|
||||
pguild_id = GUILD_NONE;
|
||||
pguild_rank = 0;
|
||||
pLFG = 0;
|
||||
gm = 0;
|
||||
pClientVersion = 0;
|
||||
for (auto& elem : tell_queue) {
|
||||
m_zone_server = 0;
|
||||
m_zone = 0;
|
||||
m_char_id = 0;
|
||||
memset(m_char_name, 0, sizeof(m_char_name));
|
||||
m_level = 0;
|
||||
m_class_ = 0;
|
||||
m_race = 0;
|
||||
m_anon = 0;
|
||||
m_tells_off = 0;
|
||||
m_guild_id = GUILD_NONE;
|
||||
m_guild_rank = 0;
|
||||
m_lfg = 0;
|
||||
m_gm = 0;
|
||||
m_client_version = 0;
|
||||
for (auto &elem: m_tell_queue) {
|
||||
safe_delete_array(elem);
|
||||
}
|
||||
tell_queue.clear();
|
||||
m_tell_queue.clear();
|
||||
}
|
||||
|
||||
void ClientListEntry::Camp(ZoneServer *iZS)
|
||||
{
|
||||
if (iZS != 0 && iZS != pzoneserver) {
|
||||
if (iZS != 0 && iZS != m_zone_server) {
|
||||
return;
|
||||
}
|
||||
if (pzoneserver) {
|
||||
pzoneserver->RemovePlayer();
|
||||
LSUpdate(pzoneserver);
|
||||
if (m_zone_server) {
|
||||
m_zone_server->RemovePlayer();
|
||||
LSUpdate(m_zone_server);
|
||||
}
|
||||
|
||||
ClearVars();
|
||||
|
||||
stale = 0;
|
||||
m_stale = 0;
|
||||
}
|
||||
|
||||
bool ClientListEntry::CheckStale()
|
||||
{
|
||||
stale++;
|
||||
if (stale > 20) {
|
||||
if (pOnline > CLE_Status::Offline) {
|
||||
m_stale++;
|
||||
if (m_stale > 20) {
|
||||
if (m_online > CLE_Status::Offline) {
|
||||
SetOnline(CLE_Status::Offline);
|
||||
}
|
||||
|
||||
@@ -324,48 +300,50 @@ bool ClientListEntry::CheckStale()
|
||||
bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password)
|
||||
{
|
||||
LogDebug(
|
||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}]",
|
||||
"ls_account_id [{}] key_password [{}] key [{}]",
|
||||
loginserver_account_id,
|
||||
key_password,
|
||||
plskey
|
||||
m_key
|
||||
);
|
||||
if (pLSID == loginserver_account_id && strncmp(plskey, key_password, 10) == 0) {
|
||||
|
||||
if (m_login_server_id == loginserver_account_id && strncmp(m_key, key_password, 10) == 0) {
|
||||
LogDebug(
|
||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}] lsid [{3}] paccountid [{4}]",
|
||||
"ls_account_id [{}] key_password [{}] m_key [{}] lsid [{}] m_account_id [{}]",
|
||||
loginserver_account_id,
|
||||
key_password,
|
||||
plskey,
|
||||
m_key,
|
||||
LSID(),
|
||||
paccountid
|
||||
m_account_id
|
||||
);
|
||||
|
||||
if (paccountid == 0 && LSID() > 0) {
|
||||
// create account if it doesn't exist
|
||||
if (m_account_id == 0 && LSID() > 0) {
|
||||
int16 default_account_status = WorldConfig::get()->DefaultStatus;
|
||||
|
||||
paccountid = database.CreateAccount(
|
||||
loginserver_account_name,
|
||||
m_account_id = database.CreateAccount(
|
||||
m_login_account_name,
|
||||
std::string(),
|
||||
default_account_status,
|
||||
source_loginserver,
|
||||
m_source_loginserver,
|
||||
LSID()
|
||||
);
|
||||
|
||||
if (!paccountid) {
|
||||
LogInfo(
|
||||
"Error adding local account for LS login: [{0}:{1}], duplicate name",
|
||||
source_loginserver,
|
||||
loginserver_account_name
|
||||
if (!m_account_id) {
|
||||
LogError(
|
||||
"Error adding local account for LS login [{}] [{}], duplicate name",
|
||||
m_source_loginserver,
|
||||
m_login_account_name
|
||||
);
|
||||
return false;
|
||||
}
|
||||
strn0cpy(paccountname, loginserver_account_name, sizeof(paccountname));
|
||||
padmin = default_account_status;
|
||||
strn0cpy(m_account_name, m_login_account_name, sizeof(m_account_name));
|
||||
m_admin = default_account_status;
|
||||
}
|
||||
std::string lsworldadmin;
|
||||
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
||||
if (Strings::ToInt(lsworldadmin) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == AccountStatus::Player)) {
|
||||
padmin = pworldadmin;
|
||||
if (Strings::ToInt(lsworldadmin) == 1 && m_world_admin != 0 &&
|
||||
(m_admin < m_world_admin || m_admin == AccountStatus::Player)) {
|
||||
m_admin = m_world_admin;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -380,8 +358,8 @@ void ClientListEntry::ProcessTellQueue()
|
||||
}
|
||||
|
||||
ServerPacket *pack;
|
||||
auto it = tell_queue.begin();
|
||||
while (it != tell_queue.end()) {
|
||||
auto it = m_tell_queue.begin();
|
||||
while (it != m_tell_queue.end()) {
|
||||
pack = new ServerPacket(
|
||||
ServerOP_ChannelMessage,
|
||||
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
||||
@@ -390,8 +368,7 @@ void ClientListEntry::ProcessTellQueue()
|
||||
Server()->SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
safe_delete_array(*it);
|
||||
it = tell_queue.erase(it);
|
||||
it = m_tell_queue.erase(it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+100
-118
@@ -8,8 +8,7 @@
|
||||
#include "../common/rulesys.h"
|
||||
#include <vector>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
Never,
|
||||
Offline,
|
||||
Online,
|
||||
@@ -18,7 +17,7 @@ typedef enum
|
||||
InZone
|
||||
} CLE_Status;
|
||||
|
||||
static const char * CLEStatusString[] = {
|
||||
static const char *CLEStatusString[] = {
|
||||
"Never",
|
||||
"Offline",
|
||||
"Online",
|
||||
@@ -33,148 +32,131 @@ struct ServerClientList_Struct;
|
||||
class ClientListEntry {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @param id
|
||||
* @param in_loginserver_id
|
||||
* @param in_loginserver_name
|
||||
* @param in_login_name
|
||||
* @param in_login_key
|
||||
* @param in_is_world_admin
|
||||
* @param ip
|
||||
* @param local
|
||||
*/
|
||||
ClientListEntry(
|
||||
uint32 id,
|
||||
uint32 in_loginserver_id,
|
||||
const char *in_loginserver_name,
|
||||
const char *in_login_name,
|
||||
const char *in_login_key,
|
||||
int16 in_is_world_admin = 0,
|
||||
uint32 ip = 0,
|
||||
uint32 login_server_id,
|
||||
const char *login_server_name,
|
||||
const char *account_name,
|
||||
const char *login_key,
|
||||
int16 is_world_admin = 0,
|
||||
uint32 ip_address = 0,
|
||||
uint8 local = 0
|
||||
);
|
||||
|
||||
/**
|
||||
* @param id
|
||||
* @param iZS
|
||||
* @param scl
|
||||
* @param iOnline
|
||||
*/
|
||||
ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = AccountStatus::Player);
|
||||
ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline);
|
||||
ClientListEntry(uint32 id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online);
|
||||
~ClientListEntry();
|
||||
bool CheckStale();
|
||||
void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, CLE_Status iOnline = CLE_Status::InZone);
|
||||
void LSUpdate(ZoneServer* zoneserver);
|
||||
void LSZoneChange(ZoneToZone_Struct* ztz);
|
||||
bool CheckAuth(uint32 loginserver_account_id, const char* key_password);
|
||||
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
||||
void SetChar(uint32 iCharID, const char* iCharName);
|
||||
inline CLE_Status Online() { return pOnline; }
|
||||
inline const uint32 GetID() const { return id; }
|
||||
inline const uint32 GetIP() const { return pIP; }
|
||||
inline void SetIP(const uint32& iIP) { pIP = iIP; }
|
||||
inline void KeepAlive() { stale = 0; }
|
||||
inline uint8 GetStaleCounter() const { return stale; }
|
||||
void LeavingZone(ZoneServer* iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
||||
void Camp(ZoneServer* iZS = 0);
|
||||
bool CheckStale();
|
||||
void Update(ZoneServer *zoneserver, ServerClientList_Struct *scl, CLE_Status iOnline = CLE_Status::InZone);
|
||||
void LSUpdate(ZoneServer *zoneserver);
|
||||
void LSZoneChange(ZoneToZone_Struct *ztz);
|
||||
bool CheckAuth(uint32 loginserver_account_id, const char *key_password);
|
||||
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
||||
void SetChar(uint32 iCharID, const char *iCharName);
|
||||
inline CLE_Status Online() { return m_online; }
|
||||
inline const uint32 GetID() const { return m_id; }
|
||||
inline const uint32 GetIP() const { return m_ip_address; }
|
||||
inline void SetIP(const uint32 &iIP) { m_ip_address = iIP; }
|
||||
inline void KeepAlive() { m_stale = 0; }
|
||||
inline uint8 GetStaleCounter() const { return m_stale; }
|
||||
void LeavingZone(ZoneServer *iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
||||
void Camp(ZoneServer *iZS = 0);
|
||||
|
||||
// Login Server stuff
|
||||
inline const char* LoginServer() const { return source_loginserver; }
|
||||
inline uint32 LSID() const { return pLSID; }
|
||||
inline uint32 LSAccountID() const { return pLSID; }
|
||||
inline const char* LSName() const { return loginserver_account_name; }
|
||||
inline int16 WorldAdmin() const { return pworldadmin; }
|
||||
inline const char* GetLSKey() const { return plskey; }
|
||||
inline const CLE_Status GetOnline() const { return pOnline; }
|
||||
inline const char *LoginServer() const { return m_source_loginserver; }
|
||||
inline uint32 LSID() const { return m_login_server_id; }
|
||||
inline uint32 LSAccountID() const { return m_login_server_id; }
|
||||
inline const char *LSName() const { return m_login_account_name; }
|
||||
inline int16 WorldAdmin() const { return m_world_admin; }
|
||||
inline const char *GetLSKey() const { return m_key; }
|
||||
inline const CLE_Status GetOnline() const { return m_online; }
|
||||
|
||||
// Account stuff
|
||||
inline uint32 AccountID() const { return paccountid; }
|
||||
inline const char* AccountName() const { return paccountname; }
|
||||
inline int16 Admin() const { return padmin; }
|
||||
inline void SetAdmin(uint16 iAdmin) { padmin = iAdmin; }
|
||||
inline uint32 AccountID() const { return m_account_id; }
|
||||
inline const char *AccountName() const { return m_account_name; }
|
||||
inline int16 Admin() const { return m_admin; }
|
||||
inline void SetAdmin(uint16 iAdmin) { m_admin = iAdmin; }
|
||||
|
||||
// Character info
|
||||
inline ZoneServer *Server() const { return pzoneserver; }
|
||||
inline void ClearServer() { pzoneserver = 0; }
|
||||
inline uint32 CharID() const { return pcharid; }
|
||||
inline const char *name() const { return pname; }
|
||||
inline uint32 zone() const { return pzone; }
|
||||
inline uint16 instance() const { return pinstance; }
|
||||
inline uint8 level() const { return plevel; }
|
||||
inline uint8 class_() const { return pclass_; }
|
||||
inline uint16 race() const { return prace; }
|
||||
inline uint8 Anon() { return panon; }
|
||||
inline uint8 TellsOff() const { return ptellsoff; }
|
||||
inline uint32 GuildID() const { return pguild_id; }
|
||||
inline uint32 GuildRank() const { return pguild_rank; }
|
||||
inline bool GuildTributeOptIn() const { return pguild_tribute_opt_in; }
|
||||
inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; }
|
||||
inline void SetGuildTributeOptIn(bool opt) { pguild_tribute_opt_in = opt; }
|
||||
inline bool LFG() const { return pLFG; }
|
||||
inline uint8 GetGM() const { return gm; }
|
||||
inline void SetGM(uint8 igm) { gm = igm; }
|
||||
inline void SetZone(uint32 zone) { pzone = zone; }
|
||||
inline bool IsLocalClient() const { return plocal; }
|
||||
inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; }
|
||||
inline uint8 GetLFGToLevel() const { return pLFGToLevel; }
|
||||
inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; }
|
||||
inline const char *GetLFGComments() const { return pLFGComments; }
|
||||
inline uint8 GetClientVersion() { return pClientVersion; }
|
||||
inline ZoneServer *Server() const { return m_zone_server; }
|
||||
inline void ClearServer() { m_zone_server = 0; }
|
||||
inline uint32 CharID() const { return m_char_id; }
|
||||
inline const char *name() const { return m_char_name; }
|
||||
inline uint32 zone() const { return m_zone; }
|
||||
inline uint16 instance() const { return m_instance; }
|
||||
inline uint8 level() const { return m_level; }
|
||||
inline uint8 class_() const { return m_class_; }
|
||||
inline uint16 race() const { return m_race; }
|
||||
inline uint8 Anon() { return m_anon; }
|
||||
inline uint8 TellsOff() const { return m_tells_off; }
|
||||
inline uint32 GuildID() const { return m_guild_id; }
|
||||
inline uint32 GuildRank() const { return m_guild_rank; }
|
||||
inline bool GuildTributeOptIn() const { return m_guild_tribute_opt_in; }
|
||||
inline void SetGuild(uint32 guild_id) { m_guild_id = guild_id; }
|
||||
inline void SetGuildTributeOptIn(bool opt) { m_guild_tribute_opt_in = opt; }
|
||||
inline bool LFG() const { return m_lfg; }
|
||||
inline uint8 GetGM() const { return m_gm; }
|
||||
inline void SetGM(uint8 igm) { m_gm = igm; }
|
||||
inline void SetZone(uint32 zone) { m_zone = zone; }
|
||||
inline bool IsLocalClient() const { return m_is_local; }
|
||||
inline uint8 GetLFGFromLevel() const { return m_lfg_from_level; }
|
||||
inline uint8 GetLFGToLevel() const { return m_lfg_to_level; }
|
||||
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
|
||||
inline const char *GetLFGComments() const { return m_lfg_comments; }
|
||||
inline uint8 GetClientVersion() { return m_client_version; }
|
||||
|
||||
inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||
inline bool TellQueueEmpty() const { return tell_queue.empty(); }
|
||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
|
||||
inline bool TellQueueFull() const { return m_tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
|
||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { m_tell_queue.push_back(scm); }
|
||||
void ProcessTellQueue();
|
||||
|
||||
void SetPendingDzInvite(ServerPacket* pack) { m_dz_invite.reset(pack->Copy()); };
|
||||
void SetPendingDzInvite(ServerPacket *pack) { m_dz_invite.reset(pack->Copy()); };
|
||||
std::unique_ptr<ServerPacket> GetPendingDzInvite() { return std::move(m_dz_invite); }
|
||||
|
||||
private:
|
||||
void ClearVars(bool iAll = false);
|
||||
void ClearVars(bool iAll = false);
|
||||
|
||||
const uint32 id;
|
||||
uint32 pIP;
|
||||
CLE_Status pOnline;
|
||||
uint8 stale;
|
||||
const uint32 m_id;
|
||||
uint32 m_ip_address;
|
||||
CLE_Status m_online;
|
||||
uint8 m_stale;
|
||||
|
||||
// Login Server stuff
|
||||
char source_loginserver[64]{}; //Loginserver we came from.
|
||||
uint32 pLSID;
|
||||
char loginserver_account_name[32]{};
|
||||
char plskey[16]{};
|
||||
int16 pworldadmin; // Login server's suggested admin status setting
|
||||
bool plocal;
|
||||
char m_source_loginserver[64]{}; //Loginserver we came from.
|
||||
uint32 m_login_server_id;
|
||||
char m_login_account_name[32]{};
|
||||
char m_key[16]{};
|
||||
int16 m_world_admin; // Login server's suggested admin status setting
|
||||
bool m_is_local;
|
||||
|
||||
// Account stuff
|
||||
uint32 paccountid;
|
||||
char paccountname[32]{};
|
||||
int16 padmin{};
|
||||
uint32 m_account_id;
|
||||
char m_account_name[32]{};
|
||||
int16 m_admin{};
|
||||
|
||||
// Character info
|
||||
ZoneServer* pzoneserver{};
|
||||
uint32 pzone{};
|
||||
uint16 pinstance{};
|
||||
uint32 pcharid{};
|
||||
char pname[64]{};
|
||||
uint8 plevel{};
|
||||
uint8 pclass_{};
|
||||
uint16 prace{};
|
||||
uint8 panon{};
|
||||
uint8 ptellsoff{};
|
||||
uint32 pguild_id{};
|
||||
uint32 pguild_rank;
|
||||
bool pguild_tribute_opt_in{};
|
||||
bool pLFG{};
|
||||
uint8 gm{};
|
||||
uint8 pClientVersion{};
|
||||
uint8 pLFGFromLevel{};
|
||||
uint8 pLFGToLevel{};
|
||||
bool pLFGMatchFilter{};
|
||||
char pLFGComments[64]{};
|
||||
ZoneServer *m_zone_server{};
|
||||
uint32 m_zone{};
|
||||
uint16 m_instance{};
|
||||
uint32 m_char_id{};
|
||||
char m_char_name[64]{};
|
||||
uint8 m_level{};
|
||||
uint8 m_class_{};
|
||||
uint16 m_race{};
|
||||
uint8 m_anon{};
|
||||
uint8 m_tells_off{};
|
||||
uint32 m_guild_id{};
|
||||
uint32 m_guild_rank;
|
||||
bool m_guild_tribute_opt_in{};
|
||||
bool m_lfg{};
|
||||
uint8 m_gm{};
|
||||
uint8 m_client_version{};
|
||||
uint8 m_lfg_from_level{};
|
||||
uint8 m_lfg_to_level{};
|
||||
bool m_lfg_match_filter{};
|
||||
char m_lfg_comments[64]{};
|
||||
|
||||
// Tell Queue -- really a vector :D
|
||||
std::vector<ServerChannelMessage_Struct *> tell_queue;
|
||||
std::vector<ServerChannelMessage_Struct *> m_tell_queue;
|
||||
|
||||
std::unique_ptr<ServerPacket> m_dz_invite;
|
||||
};
|
||||
|
||||
+23
-5
@@ -331,8 +331,26 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect
|
||||
}
|
||||
|
||||
|
||||
void ClientList::CLEAdd(uint32 iLSID, const char *iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) {
|
||||
auto tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginServerName, iLoginName, iLoginKey, iWorldAdmin, ip, local);
|
||||
void ClientList::CLEAdd(
|
||||
uint32 login_server_id,
|
||||
const char *login_server_name,
|
||||
const char *login_name,
|
||||
const char *login_key,
|
||||
int16 world_admin,
|
||||
uint32 ip_address,
|
||||
uint8 is_local
|
||||
)
|
||||
{
|
||||
auto tmp = new ClientListEntry(
|
||||
GetNextCLEID(),
|
||||
login_server_id,
|
||||
login_server_name,
|
||||
login_name,
|
||||
login_key,
|
||||
world_admin,
|
||||
ip_address,
|
||||
is_local
|
||||
);
|
||||
|
||||
clientlist.Append(tmp);
|
||||
}
|
||||
@@ -457,19 +475,19 @@ void ClientList::CLEKeepAlive(uint32 numupdates, uint32* wid) {
|
||||
}
|
||||
}
|
||||
|
||||
ClientListEntry *ClientList::CheckAuth(uint32 iLSID, const char *iKey)
|
||||
ClientListEntry *ClientList::CheckAuth(uint32 loginserver_account_id, const char *key)
|
||||
{
|
||||
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
if (iterator.GetData()->CheckAuth(iLSID, iKey)) {
|
||||
if (iterator.GetData()->CheckAuth(loginserver_account_id, key)) {
|
||||
return iterator.GetData();
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@ public:
|
||||
|
||||
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
||||
void CLERemoveZSRef(ZoneServer* iZS);
|
||||
ClientListEntry* CheckAuth(uint32 iLSID, const char* iKey);
|
||||
ClientListEntry* CheckAuth(uint32 loginserver_account_id, const char* key);
|
||||
ClientListEntry* FindCharacter(const char* name);
|
||||
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
||||
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
void DisconnectByIP(uint32 in_ip);
|
||||
void CLCheckStale();
|
||||
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
||||
void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = AccountStatus::Player, uint32 ip = 0, uint8 local=0);
|
||||
void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0);
|
||||
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
||||
bool IsAccountInGame(uint32 iLSID);
|
||||
|
||||
|
||||
+40
-40
@@ -46,8 +46,8 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
||||
const WorldConfig *Config = WorldConfig::get();
|
||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||
|
||||
UsertoWorldRequestLegacy_Struct *utwr = (UsertoWorldRequestLegacy_Struct *) p.Data();
|
||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
|
||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||
int16 status = database.GetAccountStatus(id);
|
||||
|
||||
LogDebug(
|
||||
@@ -63,11 +63,11 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
||||
|
||||
ServerPacket outpack;
|
||||
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
||||
outpack.size = sizeof(UsertoWorldResponseLegacy_Struct);
|
||||
outpack.size = sizeof(UsertoWorldResponseLegacy);
|
||||
outpack.pBuffer = new uchar[outpack.size];
|
||||
memset(outpack.pBuffer, 0, outpack.size);
|
||||
|
||||
UsertoWorldResponseLegacy_Struct *utwrs = (UsertoWorldResponseLegacy_Struct *) outpack.pBuffer;
|
||||
UsertoWorldResponseLegacy *utwrs = (UsertoWorldResponseLegacy *) outpack.pBuffer;
|
||||
utwrs->lsaccountid = utwr->lsaccountid;
|
||||
utwrs->ToID = utwr->FromID;
|
||||
utwrs->worldid = utwr->worldid;
|
||||
@@ -126,8 +126,8 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
||||
const WorldConfig *Config = WorldConfig::get();
|
||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||
|
||||
UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct *) p.Data();
|
||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
|
||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||
int16 status = database.GetAccountStatus(id);
|
||||
|
||||
LogDebug(
|
||||
@@ -143,11 +143,11 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
||||
|
||||
ServerPacket outpack;
|
||||
outpack.opcode = ServerOP_UsertoWorldResp;
|
||||
outpack.size = sizeof(UsertoWorldResponse_Struct);
|
||||
outpack.size = sizeof(UsertoWorldResponse);
|
||||
outpack.pBuffer = new uchar[outpack.size];
|
||||
memset(outpack.pBuffer, 0, outpack.size);
|
||||
|
||||
UsertoWorldResponse_Struct *utwrs = (UsertoWorldResponse_Struct *) outpack.pBuffer;
|
||||
UsertoWorldResponse *utwrs = (UsertoWorldResponse *) outpack.pBuffer;
|
||||
utwrs->lsaccountid = utwr->lsaccountid;
|
||||
utwrs->ToID = utwr->FromID;
|
||||
strn0cpy(utwrs->login, utwr->login, 64);
|
||||
@@ -208,27 +208,27 @@ void LoginServer::ProcessLSClientAuthLegacy(uint16_t opcode, EQ::Net::Packet &p)
|
||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||
|
||||
try {
|
||||
auto client_authentication_request = p.GetSerialize<ClientAuthLegacy_Struct>(0);
|
||||
auto r = p.GetSerialize<ClientAuthLegacy>(0);
|
||||
|
||||
LogDebug(
|
||||
"Processing Loginserver Auth Legacy | account_id [{0}] account_name [{1}] key [{2}] admin [{3}] ip [{4}] "
|
||||
"local_network [{5}]",
|
||||
client_authentication_request.loginserver_account_id,
|
||||
client_authentication_request.loginserver_account_name,
|
||||
client_authentication_request.key,
|
||||
client_authentication_request.is_world_admin,
|
||||
client_authentication_request.ip,
|
||||
client_authentication_request.is_client_from_local_network
|
||||
"Processing Loginserver Auth Legacy | account_id [{}] account_name [{}] key [{}] admin [{}] ip [{}] "
|
||||
"local_network [{}]",
|
||||
r.loginserver_account_id,
|
||||
r.loginserver_account_name,
|
||||
r.key,
|
||||
r.is_world_admin,
|
||||
r.ip_address,
|
||||
r.is_client_from_local_network
|
||||
);
|
||||
|
||||
client_list.CLEAdd(
|
||||
client_authentication_request.loginserver_account_id,
|
||||
r.loginserver_account_id,
|
||||
"eqemu",
|
||||
client_authentication_request.loginserver_account_name,
|
||||
client_authentication_request.key,
|
||||
client_authentication_request.is_world_admin,
|
||||
client_authentication_request.ip,
|
||||
client_authentication_request.is_client_from_local_network
|
||||
r.loginserver_account_name,
|
||||
r.key,
|
||||
r.is_world_admin,
|
||||
r.ip_address,
|
||||
r.is_client_from_local_network
|
||||
);
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
@@ -242,28 +242,28 @@ void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p)
|
||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||
|
||||
try {
|
||||
auto client_authentication_request = p.GetSerialize<ClientAuth_Struct>(0);
|
||||
auto r = p.GetSerialize<ClientAuth>(0);
|
||||
|
||||
LogDebug(
|
||||
"Processing Loginserver Auth | account_id [{0}] account_name [{1}] loginserver_name [{2}] key [{3}] "
|
||||
"admin [{4}] ip [{5}] local_network [{6}]",
|
||||
client_authentication_request.loginserver_account_id,
|
||||
client_authentication_request.account_name,
|
||||
client_authentication_request.loginserver_name,
|
||||
client_authentication_request.key,
|
||||
client_authentication_request.is_world_admin,
|
||||
client_authentication_request.ip,
|
||||
client_authentication_request.is_client_from_local_network
|
||||
"Processing Loginserver Auth | account_id [{}] account_name [{}] loginserver_name [{}] key [{}] "
|
||||
"admin [{}] ip [{}] local_network [{}]",
|
||||
r.loginserver_account_id,
|
||||
r.account_name,
|
||||
r.loginserver_name,
|
||||
r.key,
|
||||
r.is_world_admin,
|
||||
r.ip_address,
|
||||
r.is_client_from_local_network
|
||||
);
|
||||
|
||||
client_list.CLEAdd(
|
||||
client_authentication_request.loginserver_account_id,
|
||||
client_authentication_request.loginserver_name,
|
||||
client_authentication_request.account_name,
|
||||
client_authentication_request.key,
|
||||
client_authentication_request.is_world_admin,
|
||||
client_authentication_request.ip,
|
||||
client_authentication_request.is_client_from_local_network
|
||||
r.loginserver_account_id,
|
||||
r.loginserver_name,
|
||||
r.account_name,
|
||||
r.key,
|
||||
r.is_world_admin,
|
||||
r.ip_address,
|
||||
r.is_client_from_local_network
|
||||
);
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/skill_caps.h"
|
||||
#include "../common/repositories/character_parcels_repository.h"
|
||||
#include "../common/ip_util.h"
|
||||
|
||||
SkillCaps skill_caps;
|
||||
ZoneStore zone_store;
|
||||
@@ -188,6 +189,11 @@ int main(int argc, char **argv)
|
||||
launcher_list.LoadList();
|
||||
zoneserver_list.Init();
|
||||
|
||||
if (IpUtil::IsPortInUse(Config->WorldIP, Config->WorldTCPPort)) {
|
||||
LogError("World port [{}] already in use", Config->WorldTCPPort);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
||||
if (Config->TelnetEnabled) {
|
||||
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
||||
|
||||
@@ -101,6 +101,13 @@ bool WorldBoot::HandleCommandInput(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
// check if we ran a valid command, this whole CLI handler needs to be improved at a later time
|
||||
std::string arg1 = argc >= 2 ? argv[1] : "";
|
||||
if (argc >= 2 && !Strings::Contains(arg1, ":")) {
|
||||
std::cout << "Invalid command, use --help to see available commands" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,12 +35,6 @@ WorldDatabase content_db;
|
||||
extern std::vector<RaceClassAllocation> character_create_allocations;
|
||||
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
||||
|
||||
|
||||
/**
|
||||
* @param account_id
|
||||
* @param out_app
|
||||
* @param client_version_bit
|
||||
*/
|
||||
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
||||
{
|
||||
EQ::versions::ClientVersion
|
||||
|
||||
+2
-1
@@ -894,7 +894,8 @@ void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
||||
ServerReload::Type::Commands,
|
||||
ServerReload::Type::PerlExportSettings,
|
||||
ServerReload::Type::DataBucketsCache,
|
||||
ServerReload::Type::WorldRepop
|
||||
ServerReload::Type::WorldRepop,
|
||||
ServerReload::Type::WorldWithRespawn
|
||||
};
|
||||
|
||||
// Set requires_zone_booted flag before executing reload logic
|
||||
|
||||
+21
-18
@@ -2789,32 +2789,35 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
const uint16 entity_id = GetID();
|
||||
|
||||
if (
|
||||
!HasOwner() &&
|
||||
!IsMerc() &&
|
||||
!GetSwarmInfo() &&
|
||||
(!is_merchant || allow_merchant_corpse) &&
|
||||
(
|
||||
!HasOwner() &&
|
||||
!IsMerc() &&
|
||||
!GetSwarmInfo() &&
|
||||
(!is_merchant || allow_merchant_corpse) &&
|
||||
(
|
||||
killer &&
|
||||
(
|
||||
killer->IsClient() ||
|
||||
killer &&
|
||||
(
|
||||
killer->HasOwner() &&
|
||||
killer->GetUltimateOwner()->IsClient()
|
||||
) ||
|
||||
(
|
||||
killer->IsNPC() &&
|
||||
killer->CastToNPC()->GetSwarmInfo() &&
|
||||
killer->CastToNPC()->GetSwarmInfo()->GetOwner() &&
|
||||
killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()
|
||||
killer->IsClient() ||
|
||||
(
|
||||
killer->HasOwner() &&
|
||||
killer->GetUltimateOwner()->IsClient()
|
||||
) ||
|
||||
(
|
||||
killer->IsNPC() &&
|
||||
killer->CastToNPC()->GetSwarmInfo() &&
|
||||
killer->CastToNPC()->GetSwarmInfo()->GetOwner() &&
|
||||
killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()
|
||||
)
|
||||
)
|
||||
) ||
|
||||
(
|
||||
killer_mob && is_ldon_treasure
|
||||
)
|
||||
) ||
|
||||
(
|
||||
killer_mob && is_ldon_treasure
|
||||
)
|
||||
)
|
||||
) {
|
||||
|| IsQueuedForCorpse()
|
||||
) {
|
||||
if (killer) {
|
||||
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
||||
killer = killer->GetOwner();
|
||||
|
||||
+190
-400
@@ -281,443 +281,233 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_ammo_item) {
|
||||
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
||||
if (is_ammo_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsNPC() || current_level >= recommended_level) {
|
||||
b->HP += item->HP;
|
||||
b->Mana += item->Mana;
|
||||
b->Endurance += item->Endur;
|
||||
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
||||
const bool meets_recommended = (IsNPC() && !RuleB(Items, NPCUseRecommendedLevels)) || current_level >= recommended_level;
|
||||
|
||||
b->AC += item->AC;
|
||||
b->ATK += item->Attack;
|
||||
auto CalcItemBonus = [&](int statValue) -> int {
|
||||
return meets_recommended ? statValue : CalcRecommendedLevelBonus(current_level, recommended_level, statValue);
|
||||
};
|
||||
|
||||
b->STR += (item->AStr + item->HeroicStr);
|
||||
b->STA += (item->ASta + item->HeroicSta);
|
||||
b->DEX += (item->ADex + item->HeroicDex);
|
||||
b->AGI += (item->AAgi + item->HeroicAgi);
|
||||
b->INT += (item->AInt + item->HeroicInt);
|
||||
b->WIS += (item->AWis + item->HeroicWis);
|
||||
b->CHA += (item->ACha + item->HeroicCha);
|
||||
auto CalcCappedItemBonus = [&](int currentStat, int bonus, int cap) -> int {
|
||||
int calc_stat = currentStat + CalcItemBonus(bonus);
|
||||
return IsOfClientBotMerc() ? std::min(cap, calc_stat) : calc_stat;
|
||||
};
|
||||
|
||||
b->MR += (item->MR + item->HeroicMR);
|
||||
b->FR += (item->FR + item->HeroicFR);
|
||||
b->CR += (item->CR + item->HeroicCR);
|
||||
b->PR += (item->PR + item->HeroicPR);
|
||||
b->DR += (item->DR + item->HeroicDR);
|
||||
b->Corrup += (item->SVCorruption + item->HeroicSVCorrup);
|
||||
b->HP += CalcItemBonus(item->HP);
|
||||
b->Mana += CalcItemBonus(item->Mana);
|
||||
b->Endurance += CalcItemBonus(item->Endur);
|
||||
b->AC += CalcItemBonus(item->AC);
|
||||
|
||||
b->STRCapMod += item->HeroicStr;
|
||||
b->STACapMod += item->HeroicSta;
|
||||
b->DEXCapMod += item->HeroicDex;
|
||||
b->AGICapMod += item->HeroicAgi;
|
||||
b->INTCapMod += item->HeroicInt;
|
||||
b->WISCapMod += item->HeroicWis;
|
||||
b->CHACapMod += item->HeroicCha;
|
||||
b->STR += CalcItemBonus(item->AStr + item->HeroicStr);
|
||||
b->STA += CalcItemBonus(item->ASta + item->HeroicSta);
|
||||
b->DEX += CalcItemBonus(item->ADex + item->HeroicDex);
|
||||
b->AGI += CalcItemBonus(item->AAgi + item->HeroicAgi);
|
||||
b->INT += CalcItemBonus(item->AInt + item->HeroicInt);
|
||||
b->WIS += CalcItemBonus(item->AWis + item->HeroicWis);
|
||||
b->CHA += CalcItemBonus(item->ACha + item->HeroicCha);
|
||||
|
||||
b->MRCapMod += item->HeroicMR;
|
||||
b->CRCapMod += item->HeroicFR;
|
||||
b->FRCapMod += item->HeroicCR;
|
||||
b->PRCapMod += item->HeroicPR;
|
||||
b->DRCapMod += item->HeroicDR;
|
||||
b->CorrupCapMod += item->HeroicSVCorrup;
|
||||
b->HeroicSTR += CalcItemBonus(item->HeroicStr);
|
||||
b->HeroicSTA += CalcItemBonus(item->HeroicSta);
|
||||
b->HeroicDEX += CalcItemBonus(item->HeroicDex);
|
||||
b->HeroicAGI += CalcItemBonus(item->HeroicAgi);
|
||||
b->HeroicINT += CalcItemBonus(item->HeroicInt);
|
||||
b->HeroicWIS += CalcItemBonus(item->HeroicWis);
|
||||
b->HeroicCHA += CalcItemBonus(item->HeroicCha);
|
||||
|
||||
b->HeroicSTR += item->HeroicStr;
|
||||
b->HeroicSTA += item->HeroicSta;
|
||||
b->HeroicDEX += item->HeroicDex;
|
||||
b->HeroicAGI += item->HeroicAgi;
|
||||
b->HeroicINT += item->HeroicInt;
|
||||
b->HeroicWIS += item->HeroicWis;
|
||||
b->HeroicCHA += item->HeroicCha;
|
||||
b->STRCapMod += b->HeroicSTR;
|
||||
b->STACapMod += b->HeroicSTA;
|
||||
b->DEXCapMod += b->HeroicDEX;
|
||||
b->AGICapMod += b->HeroicAGI;
|
||||
b->INTCapMod += b->HeroicINT;
|
||||
b->WISCapMod += b->HeroicWIS;
|
||||
b->CHACapMod += b->HeroicCHA;
|
||||
|
||||
b->HeroicMR += item->HeroicMR;
|
||||
b->HeroicFR += item->HeroicFR;
|
||||
b->HeroicCR += item->HeroicCR;
|
||||
b->HeroicPR += item->HeroicPR;
|
||||
b->HeroicDR += item->HeroicDR;
|
||||
b->HeroicCorrup += item->HeroicSVCorrup;
|
||||
b->MR += CalcItemBonus(item->MR + item->HeroicMR);
|
||||
b->FR += CalcItemBonus(item->FR + item->HeroicFR);
|
||||
b->CR += CalcItemBonus(item->CR + item->HeroicCR);
|
||||
b->PR += CalcItemBonus(item->PR + item->HeroicPR);
|
||||
b->DR += CalcItemBonus(item->DR + item->HeroicDR);
|
||||
b->Corrup += CalcItemBonus(item->SVCorruption + item->HeroicSVCorrup);
|
||||
|
||||
b->HeroicMR += CalcItemBonus(item->HeroicMR);
|
||||
b->HeroicFR += CalcItemBonus(item->HeroicFR);
|
||||
b->HeroicCR += CalcItemBonus(item->HeroicCR);
|
||||
b->HeroicPR += CalcItemBonus(item->HeroicPR);
|
||||
b->HeroicDR += CalcItemBonus(item->HeroicDR);
|
||||
b->HeroicCorrup += CalcItemBonus(item->HeroicSVCorrup);
|
||||
|
||||
b->MRCapMod += b->HeroicMR;
|
||||
b->FRCapMod += b->HeroicFR;
|
||||
b->CRCapMod += b->HeroicCR;
|
||||
b->PRCapMod += b->HeroicPR;
|
||||
b->DRCapMod += b->HeroicDR;
|
||||
b->CorrupCapMod += b->HeroicCorrup;
|
||||
|
||||
b->HPRegen += CalcItemBonus(item->Regen);
|
||||
b->ManaRegen += CalcItemBonus(item->ManaRegen);
|
||||
b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen);
|
||||
|
||||
// These have rule-configured caps.
|
||||
b->ATK = CalcCappedItemBonus(b->ATK, item->Attack, RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap);
|
||||
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, RuleI(Character, ItemDamageShieldCap));
|
||||
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, RuleI(Character, ItemSpellShieldingCap));
|
||||
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, RuleI(Character, ItemShieldingCap));
|
||||
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, RuleI(Character, ItemStunResistCap));
|
||||
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, RuleI(Character, ItemStrikethroughCap));
|
||||
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, RuleI(Character, ItemAvoidanceCap));
|
||||
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, RuleI(Character, ItemAccuracyCap));
|
||||
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, RuleI(Character, ItemCombatEffectsCap));
|
||||
b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, RuleI(Character, ItemDoTShieldingCap));
|
||||
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, RuleI(Character, ItemHealAmtCap));
|
||||
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, RuleI(Character, ItemSpellDmgCap));
|
||||
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, RuleI(Character, ItemClairvoyanceCap));
|
||||
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, RuleI(Character, ItemDSMitigationCap));
|
||||
|
||||
if (b->haste < item->Haste) {
|
||||
b->haste = item->Haste;
|
||||
}
|
||||
|
||||
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
||||
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
||||
for (const auto &skill_id: EQ::skills::GetExtraDamageSkills()) {
|
||||
b->SkillDamageAmount[skill_id] = CalcCappedItemBonus(b->SkillDamageAmount[skill_id], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||
}
|
||||
} else {
|
||||
b->HP += CalcRecommendedLevelBonus(current_level, recommended_level, item->HP);
|
||||
b->Mana += CalcRecommendedLevelBonus(current_level, recommended_level, item->Mana);
|
||||
b->Endurance += CalcRecommendedLevelBonus(current_level, recommended_level, item->Endur);
|
||||
|
||||
b->AC += CalcRecommendedLevelBonus(current_level, recommended_level, item->AC);
|
||||
b->ATK += CalcRecommendedLevelBonus(current_level, recommended_level, item->Attack);
|
||||
|
||||
b->STR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AStr + item->HeroicStr));
|
||||
b->STA += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ASta + item->HeroicSta));
|
||||
b->DEX += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ADex + item->HeroicDex));
|
||||
b->AGI += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AAgi + item->HeroicAgi));
|
||||
b->INT += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AInt + item->HeroicInt));
|
||||
b->WIS += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AWis + item->HeroicWis));
|
||||
b->CHA += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ACha + item->HeroicCha));
|
||||
|
||||
b->MR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->MR + item->HeroicMR));
|
||||
b->FR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->FR + item->HeroicFR));
|
||||
b->CR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->CR + item->HeroicCR));
|
||||
b->PR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->PR + item->HeroicPR));
|
||||
b->DR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->DR + item->HeroicDR));
|
||||
b->Corrup += CalcRecommendedLevelBonus(current_level, recommended_level, (item->SVCorruption + item->HeroicSVCorrup));
|
||||
|
||||
b->STRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicStr);
|
||||
b->STACapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSta);
|
||||
b->DEXCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDex);
|
||||
b->AGICapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicAgi);
|
||||
b->INTCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicInt);
|
||||
b->WISCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicWis);
|
||||
b->CHACapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCha);
|
||||
|
||||
b->MRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicMR);
|
||||
b->CRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicFR);
|
||||
b->FRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCR);
|
||||
b->PRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicPR);
|
||||
b->DRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDR);
|
||||
b->CorrupCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSVCorrup);
|
||||
|
||||
b->HeroicSTR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicStr);
|
||||
b->HeroicSTA += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSta);
|
||||
b->HeroicDEX += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDex);
|
||||
b->HeroicAGI += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicAgi);
|
||||
b->HeroicINT += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicInt);
|
||||
b->HeroicWIS += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicWis);
|
||||
b->HeroicCHA += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCha);
|
||||
|
||||
b->HeroicMR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicMR);
|
||||
b->HeroicFR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicFR);
|
||||
b->HeroicCR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCR);
|
||||
b->HeroicPR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicPR);
|
||||
b->HeroicDR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDR);
|
||||
b->HeroicCorrup += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSVCorrup);
|
||||
b->SkillDamageAmount[item->ExtraDmgSkill] = CalcCappedItemBonus(b->SkillDamageAmount[item->ExtraDmgSkill], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||
}
|
||||
}
|
||||
|
||||
if (b->haste < item->Haste) {
|
||||
b->haste = item->Haste;
|
||||
if (item->Worn.Effect > 0 && item->Worn.Type == EQ::item::ItemEffectWorn) {
|
||||
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, b, 0, item->Worn.Type);
|
||||
}
|
||||
|
||||
if (item->Focus.Effect > 0 && item->Focus.Type == EQ::item::ItemEffectFocus) {
|
||||
if (
|
||||
IsOfClientBotMerc() ||
|
||||
(IsNPC() && RuleB(Spells, NPC_UseFocusFromItems))
|
||||
) {
|
||||
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, b, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Regen != 0) {
|
||||
b->HPRegen += item->Regen;
|
||||
switch (item->BardType) {
|
||||
case EQ::item::ItemTypeAllInstrumentTypes: { // (e.g. Singing Short Sword)
|
||||
if (item->BardValue > b->singingMod) {
|
||||
b->singingMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->brassMod) {
|
||||
b->brassMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->stringedMod) {
|
||||
b->stringedMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->percussionMod) {
|
||||
b->percussionMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->windMod) {
|
||||
b->windMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeSinging: {
|
||||
if (item->BardValue > b->singingMod) {
|
||||
b->singingMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->ManaRegen != 0) {
|
||||
b->ManaRegen += item->ManaRegen;
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeWindInstrument: {
|
||||
if (item->BardValue > b->windMod) {
|
||||
b->windMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->EnduranceRegen != 0) {
|
||||
b->EnduranceRegen += item->EnduranceRegen;
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeStringedInstrument: {
|
||||
if (item->BardValue > b->stringedMod) {
|
||||
b->stringedMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->Attack != 0) {
|
||||
unsigned int cap = RuleI(Character, ItemATKCap);
|
||||
cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap;
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeBrassInstrument: {
|
||||
if (item->BardValue > b->brassMod) {
|
||||
b->brassMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->ATK + item->Attack) > cap
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypePercussionInstrument: {
|
||||
if (item->BardValue > b->percussionMod) {
|
||||
b->percussionMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->SkillModValue != 0 && item->SkillModType <= EQ::skills::HIGHEST_SKILL) {
|
||||
if (
|
||||
(item->SkillModValue > 0 && b->skillmod[item->SkillModType] < item->SkillModValue) ||
|
||||
(item->SkillModValue < 0 && b->skillmod[item->SkillModType] > item->SkillModValue)
|
||||
) {
|
||||
b->ATK = RuleI(Character, ItemATKCap);
|
||||
} else {
|
||||
b->ATK += item->Attack;
|
||||
}
|
||||
b->skillmod[item->SkillModType] = item->SkillModValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->DamageShield != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)
|
||||
) {
|
||||
b->DamageShield = RuleI(Character, ItemDamageShieldCap);
|
||||
} else {
|
||||
b->DamageShield += item->DamageShield;
|
||||
}
|
||||
if (item->FactionMod1) {
|
||||
if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) {
|
||||
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
||||
} else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) {
|
||||
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->SpellShield != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)
|
||||
) {
|
||||
b->SpellShield = RuleI(Character, ItemSpellShieldingCap);
|
||||
} else {
|
||||
b->SpellShield += item->SpellShield;
|
||||
}
|
||||
if (item->FactionMod2) {
|
||||
if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) {
|
||||
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
||||
} else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) {
|
||||
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Shielding != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)
|
||||
) {
|
||||
b->MeleeMitigation = RuleI(Character, ItemShieldingCap);
|
||||
} else {
|
||||
b->MeleeMitigation += item->Shielding;
|
||||
}
|
||||
if (item->FactionMod3) {
|
||||
if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) {
|
||||
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
||||
} else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) {
|
||||
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->StunResist != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)
|
||||
) {
|
||||
b->StunResist = RuleI(Character, ItemStunResistCap);
|
||||
} else {
|
||||
b->StunResist += item->StunResist;
|
||||
}
|
||||
if (item->FactionMod4) {
|
||||
if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) {
|
||||
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
||||
} else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) {
|
||||
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->StrikeThrough != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)
|
||||
) {
|
||||
b->StrikeThrough = RuleI(Character, ItemStrikethroughCap);
|
||||
} else {
|
||||
b->StrikeThrough += item->StrikeThrough;
|
||||
if (!is_augment) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
const auto* augment = inst->GetAugment(i);
|
||||
if (!augment) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Avoidance != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)
|
||||
) {
|
||||
b->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap);
|
||||
} else {
|
||||
b->AvoidMeleeChance += item->Avoidance;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Accuracy != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)
|
||||
) {
|
||||
b->HitChance = RuleI(Character, ItemAccuracyCap);
|
||||
} else {
|
||||
b->HitChance += item->Accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->CombatEffects != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)
|
||||
) {
|
||||
b->ProcChance = RuleI(Character, ItemCombatEffectsCap);
|
||||
} else {
|
||||
b->ProcChance += item->CombatEffects;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->DotShielding != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)
|
||||
) {
|
||||
b->DoTShielding = RuleI(Character, ItemDoTShieldingCap);
|
||||
} else {
|
||||
b->DoTShielding += item->DotShielding;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->HealAmt != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)
|
||||
) {
|
||||
b->HealAmt = RuleI(Character, ItemHealAmtCap);
|
||||
} else {
|
||||
b->HealAmt += item->HealAmt;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->SpellDmg != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)
|
||||
) {
|
||||
b->SpellDmg = RuleI(Character, ItemSpellDmgCap);
|
||||
} else {
|
||||
b->SpellDmg += item->SpellDmg;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Clairvoyance != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)
|
||||
) {
|
||||
b->Clairvoyance = RuleI(Character, ItemClairvoyanceCap);
|
||||
} else {
|
||||
b->Clairvoyance += item->Clairvoyance;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->DSMitigation != 0) {
|
||||
if (
|
||||
IsOfClientBotMerc() &&
|
||||
(b->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)
|
||||
) {
|
||||
b->DSMitigation = RuleI(Character, ItemDSMitigationCap);
|
||||
} else {
|
||||
b->DSMitigation += item->DSMitigation;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Worn.Effect > 0 && item->Worn.Type == EQ::item::ItemEffectWorn) {
|
||||
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, b, 0, item->Worn.Type);
|
||||
}
|
||||
|
||||
if (item->Focus.Effect > 0 && item->Focus.Type == EQ::item::ItemEffectFocus) {
|
||||
if (
|
||||
IsOfClientBotMerc() ||
|
||||
(IsNPC() && RuleB(Spells, NPC_UseFocusFromItems))
|
||||
) {
|
||||
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, b, 0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (item->BardType) {
|
||||
case EQ::item::ItemTypeAllInstrumentTypes: { // (e.g. Singing Short Sword)
|
||||
if (item->BardValue > b->singingMod) {
|
||||
b->singingMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->brassMod) {
|
||||
b->brassMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->stringedMod) {
|
||||
b->stringedMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->percussionMod) {
|
||||
b->percussionMod = item->BardValue;
|
||||
}
|
||||
|
||||
if (item->BardValue > b->windMod) {
|
||||
b->windMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeSinging: {
|
||||
if (item->BardValue > b->singingMod) {
|
||||
b->singingMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeWindInstrument: {
|
||||
if (item->BardValue > b->windMod) {
|
||||
b->windMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeStringedInstrument: {
|
||||
if (item->BardValue > b->stringedMod) {
|
||||
b->stringedMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypeBrassInstrument: {
|
||||
if (item->BardValue > b->brassMod) {
|
||||
b->brassMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EQ::item::ItemTypePercussionInstrument: {
|
||||
if (item->BardValue > b->percussionMod) {
|
||||
b->percussionMod = item->BardValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->SkillModValue != 0 && item->SkillModType <= EQ::skills::HIGHEST_SKILL) {
|
||||
if (
|
||||
(item->SkillModValue > 0 && b->skillmod[item->SkillModType] < item->SkillModValue) ||
|
||||
(item->SkillModValue < 0 && b->skillmod[item->SkillModType] > item->SkillModValue)
|
||||
) {
|
||||
b->skillmod[item->SkillModType] = item->SkillModValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->FactionMod1) {
|
||||
if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) {
|
||||
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
||||
} else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) {
|
||||
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->FactionMod2) {
|
||||
if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) {
|
||||
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
||||
} else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) {
|
||||
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->FactionMod3) {
|
||||
if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) {
|
||||
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
||||
} else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) {
|
||||
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->FactionMod4) {
|
||||
if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) {
|
||||
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
||||
} else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) {
|
||||
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
||||
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
||||
for (const auto& skill_id : EQ::skills::GetExtraDamageSkills()) {
|
||||
if (
|
||||
!IsNPC() &&
|
||||
RuleI(Character, ItemExtraDmgCap) >= 0 &&
|
||||
(b->SkillDamageAmount[skill_id] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)
|
||||
) {
|
||||
b->SkillDamageAmount[skill_id] = RuleI(Character, ItemExtraDmgCap);
|
||||
} else {
|
||||
b->SkillDamageAmount[skill_id] += item->ExtraDmgAmt;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!IsNPC() &&
|
||||
RuleI(Character, ItemExtraDmgCap) >= 0 &&
|
||||
(b->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)
|
||||
) {
|
||||
b->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap);
|
||||
} else {
|
||||
b->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_augment) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
const auto* augment = inst->GetAugment(i);
|
||||
if (!augment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddItemBonuses(augment, b, true, false, recommended_level);
|
||||
}
|
||||
AddItemBonuses(augment, b, true, false, recommended_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
||||
/*
|
||||
Powerful Non-live like option allows developers to add worn effects on items that
|
||||
|
||||
+2
-2
@@ -3728,7 +3728,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
||||
SetVerifiedRaid(true);
|
||||
}
|
||||
}
|
||||
else if (auto group = entity_list.GetGroupByMob(this)) {
|
||||
else if (auto group = entity_list.GetGroupByMobName(GetName())) {
|
||||
// Safety Check to confirm we have a valid group
|
||||
auto owner = GetBotOwner();
|
||||
if (owner && !group->IsGroupMember(owner->GetCleanName())) {
|
||||
@@ -3744,7 +3744,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
||||
|
||||
if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) {
|
||||
OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
||||
CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database
|
||||
CheckBotSpells(); //This runs through a series of checks and outputs any spells that are set to the wrong spell type in the database
|
||||
}
|
||||
|
||||
if (IsBotRanged()) {
|
||||
|
||||
+54
-21
@@ -708,6 +708,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
entity_list.RemoveMobFromCloseLists(this);
|
||||
m_close_mobs.clear();
|
||||
|
||||
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB (Parcel, EnableParcelMerchants)) {
|
||||
DoParcelCancel();
|
||||
}
|
||||
@@ -965,6 +968,10 @@ bool Client::SaveAA()
|
||||
|
||||
m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa;
|
||||
|
||||
if (v.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v);
|
||||
}
|
||||
|
||||
@@ -4622,7 +4629,7 @@ void Client::KeyRingLoad()
|
||||
const auto &l = KeyringRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`char_id` = {} ORDER BY `item_id`",
|
||||
"`char_id` = {} ORDER BY `item_id` ASC",
|
||||
character_id
|
||||
)
|
||||
);
|
||||
@@ -4631,21 +4638,15 @@ void Client::KeyRingLoad()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (const auto &e : l) {
|
||||
for (const auto& e : l) {
|
||||
keyring.emplace_back(e.item_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::KeyRingAdd(uint32 item_id)
|
||||
bool Client::KeyRingAdd(uint32 item_id)
|
||||
{
|
||||
if (!item_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool found = KeyRingCheck(item_id);
|
||||
if (found) {
|
||||
return;
|
||||
if (!item_id || KeyRingCheck(item_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = KeyringRepository::NewEntity();
|
||||
@@ -4656,14 +4657,14 @@ void Client::KeyRingAdd(uint32 item_id)
|
||||
e = KeyringRepository::InsertOne(database, e);
|
||||
|
||||
if (!e.id) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
keyring.emplace_back(item_id);
|
||||
|
||||
if (!RuleB(World, UseItemLinksForKeyRing)) {
|
||||
Message(Chat::LightBlue, "Added to keyring.");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string &item_link = database.CreateItemLink(item_id);
|
||||
@@ -4675,17 +4676,25 @@ void Client::KeyRingAdd(uint32 item_id)
|
||||
item_link
|
||||
).c_str()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::KeyRingCheck(uint32 item_id)
|
||||
{
|
||||
for (const auto &e : keyring) {
|
||||
if (e == item_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return std::find(keyring.begin(), keyring.end(), item_id) != keyring.end();
|
||||
}
|
||||
|
||||
return false;
|
||||
bool Client::KeyRingClear()
|
||||
{
|
||||
keyring.clear();
|
||||
|
||||
return KeyringRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`char_id` = {}",
|
||||
CharacterID()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void Client::KeyRingList()
|
||||
@@ -4694,16 +4703,40 @@ void Client::KeyRingList()
|
||||
|
||||
const EQ::ItemData *item = nullptr;
|
||||
|
||||
for (const auto &e : keyring) {
|
||||
for (const uint32& e : keyring) {
|
||||
item = database.GetItem(e);
|
||||
if (item) {
|
||||
const std::string &item_string = RuleB(World, UseItemLinksForKeyRing) ? database.CreateItemLink(e) : item->Name;
|
||||
const std::string& item_string = (
|
||||
RuleB(World, UseItemLinksForKeyRing) ?
|
||||
database.CreateItemLink(e) :
|
||||
item->Name
|
||||
);
|
||||
|
||||
Message(Chat::LightBlue, item_string.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::KeyRingRemove(uint32 item_id)
|
||||
{
|
||||
keyring.erase(
|
||||
std::remove(
|
||||
keyring.begin(),
|
||||
keyring.end(),
|
||||
item_id
|
||||
)
|
||||
);
|
||||
|
||||
return KeyringRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`char_id` = {} AND `item_id` = {}",
|
||||
CharacterID(),
|
||||
item_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool Client::IsPetNameChangeAllowed() {
|
||||
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
||||
return true;
|
||||
|
||||
+33
-3
@@ -327,8 +327,10 @@ public:
|
||||
// void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||
uint8 WithCustomer(uint16 NewCustomer);
|
||||
void KeyRingLoad();
|
||||
void KeyRingAdd(uint32 item_id);
|
||||
bool KeyRingAdd(uint32 item_id);
|
||||
bool KeyRingCheck(uint32 item_id);
|
||||
bool KeyRingClear();
|
||||
bool KeyRingRemove(uint32 item_id);
|
||||
void KeyRingList();
|
||||
bool IsPetNameChangeAllowed();
|
||||
void GrantPetNameChange();
|
||||
@@ -1289,6 +1291,29 @@ public:
|
||||
void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false);
|
||||
|
||||
// Task System Methods
|
||||
inline void LoadClientSharedCompletedTasks()
|
||||
{
|
||||
std::string query = fmt::format(R"(
|
||||
SELECT
|
||||
cst.task_id
|
||||
FROM completed_shared_task_members cstm
|
||||
JOIN completed_shared_tasks cst ON cstm.shared_task_id = cst.id
|
||||
WHERE cstm.character_id = {}
|
||||
GROUP BY cst.task_id;
|
||||
)", CharacterID());
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_completed_shared_tasks.clear();
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
m_completed_shared_tasks.push_back(std::stoi(row[0]));
|
||||
}
|
||||
};
|
||||
inline std::vector<uint32_t> GetCompletedSharedTasks() const { return m_completed_shared_tasks; };
|
||||
void LoadClientTaskState();
|
||||
void RemoveClientTaskState();
|
||||
void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1);
|
||||
@@ -1459,7 +1484,10 @@ public:
|
||||
{
|
||||
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
|
||||
}
|
||||
inline bool IsTaskCompleted(int task_id) { return (task_state ? task_state->IsTaskCompleted(task_id) : false); }
|
||||
inline bool IsTaskCompleted(int task_id)
|
||||
{
|
||||
return (task_state ? task_state->IsTaskCompleted(task_id, this) : false);
|
||||
}
|
||||
inline bool AreTasksCompleted(std::vector<int> task_ids)
|
||||
{
|
||||
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
|
||||
@@ -2012,7 +2040,7 @@ private:
|
||||
bool GuildBanker;
|
||||
uint16 duel_target;
|
||||
bool duelaccepted;
|
||||
std::list<uint32> keyring;
|
||||
std::vector<uint32> keyring;
|
||||
bool tellsoff; // GM /toggle
|
||||
bool gm_hide_me;
|
||||
bool LFG;
|
||||
@@ -2290,6 +2318,8 @@ private:
|
||||
bool m_has_quest_compass = false;
|
||||
std::vector<uint32_t> m_dynamic_zone_ids;
|
||||
|
||||
std::vector<uint32_t> m_completed_shared_tasks;
|
||||
|
||||
public:
|
||||
enum BotOwnerOption : size_t {
|
||||
booDeathMarquee,
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#include "worldserver.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
extern QueryServ* QServ;
|
||||
extern QueryServ* QServ;
|
||||
const std::string SUB_TYPE_DELIMITER = ".";
|
||||
|
||||
void Client::DoEvolveItemToggle(const EQApplicationPacket *app)
|
||||
{
|
||||
@@ -77,11 +78,12 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
|
||||
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
|
||||
LogEvolveItemDetail(
|
||||
"CharacterID <green>[{}], item ID <yellow>[{}] not an evolving item.", CharacterID(), inst->GetID());
|
||||
"CharacterID <green>[{}], item ID <yellow>[{}] not an evolving item.", CharacterID(), inst->GetID()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) {
|
||||
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check(false)) {
|
||||
LogEvolveItemDetail(
|
||||
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] secs remaining.",
|
||||
CharacterID(),
|
||||
@@ -98,32 +100,39 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
CharacterID(),
|
||||
inst->GetID(),
|
||||
type,
|
||||
sub_type);
|
||||
sub_type
|
||||
);
|
||||
|
||||
auto sub_types = Strings::Split(sub_type, SUB_TYPE_DELIMITER);
|
||||
auto has_sub_type = [&](uint32_t type) {
|
||||
return Strings::Contains(sub_types, std::to_string(type));
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case EvolvingItems::Types::AMOUNT_OF_EXP: {
|
||||
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
|
||||
if (sub_type == EvolvingItems::SubTypes::ALL_EXP ||
|
||||
(sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) {
|
||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100);
|
||||
|
||||
// Determine the evolve amount based on sub_type conditions
|
||||
int evolve_amount = 0;
|
||||
|
||||
if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||
(has_sub_type(EvolvingItems::SubTypes::GROUP_EXP) && IsGrouped())) {
|
||||
evolve_amount = exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100;
|
||||
}
|
||||
else if (
|
||||
sub_type == EvolvingItems::SubTypes::ALL_EXP ||
|
||||
(sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) {
|
||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100);
|
||||
else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||
(has_sub_type(EvolvingItems::SubTypes::RAID_EXP) && IsRaidGrouped())) {
|
||||
evolve_amount = exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100;
|
||||
}
|
||||
else if (
|
||||
sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) {
|
||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100);
|
||||
else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||
has_sub_type(EvolvingItems::SubTypes::SOLO_EXP)) {
|
||||
evolve_amount = exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100;
|
||||
}
|
||||
|
||||
inst->CalculateEvolveProgression();
|
||||
|
||||
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
||||
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression());
|
||||
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression()
|
||||
);
|
||||
if (!e.id) {
|
||||
break;
|
||||
}
|
||||
@@ -146,7 +155,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
}
|
||||
case EvolvingItems::Types::SPECIFIC_MOB_RACE: {
|
||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
||||
if (mob && mob->GetRace() == sub_type) {
|
||||
if (mob && has_sub_type(mob->GetRace())) {
|
||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||
inst->SetEvolveAddToCurrentAmount(1);
|
||||
inst->CalculateEvolveProgression();
|
||||
@@ -155,7 +164,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
database,
|
||||
inst->GetEvolveUniqueID(),
|
||||
inst->GetEvolveCurrentAmount(),
|
||||
inst->GetEvolveProgression());
|
||||
inst->GetEvolveProgression()
|
||||
);
|
||||
if (!e.id) {
|
||||
break;
|
||||
}
|
||||
@@ -163,7 +173,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||
|
||||
LogEvolveItem(
|
||||
"Processing Complete for item id <green>[{1}] Type 3 Specific Mob Race - SubType <yellow>[{0}] "
|
||||
"Processing Complete for item id <green>[{1}] Type 3 Specific Mob Race - SubType "
|
||||
"<yellow>[{0}] "
|
||||
"- Increased count by 1 for <green>[{1}]",
|
||||
sub_type,
|
||||
inst->GetID()
|
||||
@@ -178,7 +189,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
}
|
||||
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
|
||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
||||
if (mob && mob->GetZoneID() == sub_type) {
|
||||
if (mob && has_sub_type(mob->GetZoneID())) {
|
||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||
inst->SetEvolveAddToCurrentAmount(1);
|
||||
inst->CalculateEvolveProgression();
|
||||
@@ -187,7 +198,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
database,
|
||||
inst->GetEvolveUniqueID(),
|
||||
inst->GetEvolveCurrentAmount(),
|
||||
inst->GetEvolveProgression());
|
||||
inst->GetEvolveProgression()
|
||||
);
|
||||
if (!e.id) {
|
||||
break;
|
||||
}
|
||||
@@ -195,7 +207,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||
|
||||
LogEvolveItem(
|
||||
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType <yellow>[{0}] "
|
||||
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType "
|
||||
"<yellow>[{0}] "
|
||||
"- Increased count by 1 for <green>[{1}]",
|
||||
sub_type,
|
||||
inst->GetID()
|
||||
@@ -208,6 +221,44 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||
|
||||
break;
|
||||
}
|
||||
case EvolvingItems::Types::NUMBER_OF_KILLS: {
|
||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
||||
if (mob) {
|
||||
if (mob->GetLevel() >= Strings::ToUnsignedInt(sub_types.front()) ||
|
||||
Strings::ToUnsignedInt(sub_types.front()) == 0
|
||||
) {
|
||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||
inst->SetEvolveAddToCurrentAmount(1);
|
||||
inst->CalculateEvolveProgression();
|
||||
|
||||
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
||||
database,
|
||||
inst->GetEvolveUniqueID(),
|
||||
inst->GetEvolveCurrentAmount(),
|
||||
inst->GetEvolveProgression()
|
||||
);
|
||||
if (!e.id) {
|
||||
break;
|
||||
}
|
||||
|
||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||
|
||||
LogEvolveItem(
|
||||
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType "
|
||||
"<yellow>[{0}] "
|
||||
"- Increased count by 1 for <green>[{1}]",
|
||||
sub_type,
|
||||
inst->GetID()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->GetEvolveProgression() >= 100) {
|
||||
queue.push_back(inst);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "../common/types.h"
|
||||
#include "../common/spdat.h"
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
|
||||
#define HIGHEST_RESIST 9 //Max resist type value
|
||||
#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob.
|
||||
|
||||
@@ -272,6 +274,46 @@ struct Buffs_Struct {
|
||||
bool persistant_buff;
|
||||
bool client; //True if the caster is a client
|
||||
bool UpdateClient;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
|
||||
std::string caster_name_str(caster_name);
|
||||
if (Archive::is_saving::value) {
|
||||
caster_name_str = std::string(caster_name);
|
||||
}
|
||||
|
||||
ar(
|
||||
CEREAL_NVP(spellid),
|
||||
CEREAL_NVP(casterlevel),
|
||||
CEREAL_NVP(casterid),
|
||||
CEREAL_NVP(caster_name_str),
|
||||
CEREAL_NVP(ticsremaining),
|
||||
CEREAL_NVP(counters),
|
||||
CEREAL_NVP(hit_number),
|
||||
CEREAL_NVP(melee_rune),
|
||||
CEREAL_NVP(magic_rune),
|
||||
CEREAL_NVP(dot_rune),
|
||||
CEREAL_NVP(caston_x),
|
||||
CEREAL_NVP(caston_y),
|
||||
CEREAL_NVP(caston_z),
|
||||
CEREAL_NVP(ExtraDIChance),
|
||||
CEREAL_NVP(RootBreakChance),
|
||||
CEREAL_NVP(instrument_mod),
|
||||
CEREAL_NVP(virus_spread_time),
|
||||
CEREAL_NVP(persistant_buff),
|
||||
CEREAL_NVP(client),
|
||||
CEREAL_NVP(UpdateClient)
|
||||
);
|
||||
|
||||
// Copy back into caster_name after deserialization
|
||||
if (Archive::is_loading::value) {
|
||||
strncpy(caster_name, caster_name_str.c_str(), sizeof(caster_name));
|
||||
caster_name[sizeof(caster_name) - 1] = '\0'; // Ensure null termination
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct StatBonuses {
|
||||
|
||||
@@ -200,6 +200,7 @@ public:
|
||||
uint32 GetItemIDBySlot(uint16 loot_slot);
|
||||
uint16 GetFirstLootSlotByItemID(uint32 item_id);
|
||||
std::vector<int> GetLootList();
|
||||
inline const LootItems &GetLootItems() { return m_item_list; }
|
||||
void LootCorpseItem(Client *c, const EQApplicationPacket *app);
|
||||
void EndLoot(Client *c, const EQApplicationPacket *app);
|
||||
void MakeLootRequestPackets(Client *c, const EQApplicationPacket *app);
|
||||
|
||||
+24
-1
@@ -754,7 +754,7 @@ void Perl__setsky(uint8 new_sky)
|
||||
|
||||
void Perl__setguild(uint32_t guild_id, uint8_t guild_rank_id)
|
||||
{
|
||||
quest_manager.setguild(guild_id, guild_rank_id);
|
||||
quest_manager.SetGuild(guild_id, guild_rank_id);
|
||||
}
|
||||
|
||||
void Perl__createguild(const char* guild_name, const char* leader_name)
|
||||
@@ -5973,6 +5973,28 @@ void Perl__SpawnGrid(uint32 npc_id, float x, float y, float z, float heading, fl
|
||||
quest_manager.SpawnGrid(npc_id, glm::vec4(x, y, z, heading), spacing, spawn_count);
|
||||
}
|
||||
|
||||
bool Perl__handin(perl::reference handin_ref)
|
||||
{
|
||||
perl::hash handin = handin_ref;
|
||||
|
||||
std::map<std::string, uint32> handin_map;
|
||||
|
||||
for (auto e: handin) {
|
||||
if (!e.first) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Strings::EqualFold(e.first, "0")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32 count = static_cast<uint32>(handin.at(e.first));
|
||||
handin_map[e.first] = count;
|
||||
}
|
||||
|
||||
return quest_manager.handin(handin_map);
|
||||
}
|
||||
|
||||
void perl_register_quest()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -6698,6 +6720,7 @@ void perl_register_quest()
|
||||
package.add("gmsay", (void(*)(const char*, int, bool))&Perl__gmsay);
|
||||
package.add("gmsay", (void(*)(const char*, int, bool, int))&Perl__gmsay);
|
||||
package.add("gmsay", (void(*)(const char*, int, bool, int, int))&Perl__gmsay);
|
||||
package.add("handin", &Perl__handin);
|
||||
package.add("has_zone_flag", &Perl__has_zone_flag);
|
||||
package.add("hasrecipelearned", &Perl__hasrecipelearned);
|
||||
package.add("hastimer", &Perl__hastimer);
|
||||
|
||||
+2
-2
@@ -2231,7 +2231,7 @@ Raid* EntityList::GetRaidByBotName(const char* name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -2933,7 +2933,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
||||
for (auto &e : mob_list) {
|
||||
auto mob = e.second;
|
||||
|
||||
if (mob->GetID() <= 0) {
|
||||
if (mob && mob->GetID() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,8 @@ void command_guild(Client* c, const Seperator* sep)
|
||||
else {
|
||||
auto guild_name = sep->argplus[3];
|
||||
auto guild_id = guild_mgr.CreateGuild(sep->argplus[3], leader_id);
|
||||
auto leader = entity_list.GetClientByCharID(leader_id);
|
||||
|
||||
|
||||
LogGuilds(
|
||||
"[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]",
|
||||
@@ -115,7 +117,7 @@ void command_guild(Client* c, const Seperator* sep)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (!guild_mgr.SetGuild(leader_id, guild_id, GUILD_LEADER)) {
|
||||
if (!guild_mgr.SetGuild(leader, guild_id, GUILD_LEADER)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
@@ -268,6 +270,7 @@ void command_guild(Client* c, const Seperator* sep)
|
||||
database.GetCharacterID(sep->arg[2])
|
||||
);
|
||||
auto character_name = database.GetCharNameByID(character_id);
|
||||
auto client = entity_list.GetClientByCharID(character_id);
|
||||
if (!character_id || character_name.empty()) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
@@ -305,14 +308,14 @@ void command_guild(Client* c, const Seperator* sep)
|
||||
"{} ({}) has {} put into {} ({}).",
|
||||
character_name,
|
||||
character_id,
|
||||
guild_mgr.SetGuild(character_id, guild_id, GUILD_MEMBER) ? "been" : "failed to be",
|
||||
guild_mgr.SetGuild(client, guild_id, GUILD_MEMBER) ? "been" : "failed to be",
|
||||
guild_mgr.GetGuildNameByID(guild_id),
|
||||
guild_id
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
guild_mgr.SetGuild(character_id, GUILD_NONE, 0);
|
||||
guild_mgr.SetGuild(client, GUILD_NONE, 0);
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
|
||||
+35
-36
@@ -1317,42 +1317,6 @@ bool GuildBankManager::SplitStack(uint32 guild_id, uint16 slot_id, uint32 quanti
|
||||
return true;
|
||||
}
|
||||
|
||||
// void GuildBankManager::UpdateItemQuantity(uint32 guildID, uint16 area, uint16 slotID, uint32 quantity)
|
||||
// {
|
||||
// // Helper method for MergeStacks. Assuming all passed parameters are valid.
|
||||
// //
|
||||
// std::string query = StringFormat("UPDATE `guild_bank` SET `qty` = %i "
|
||||
// "WHERE `guildid` = %i AND `area` = %i "
|
||||
// "AND `slot` = %i LIMIT 1",
|
||||
// quantity, guildID, area, slotID);
|
||||
// auto results = database.QueryDatabase(query);
|
||||
// if(!results.Success()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
// bool GuildBankManager::AllowedToWithdraw(uint32 guild_id, uint16 area, uint16 slot_id, const char *name)
|
||||
// {
|
||||
// auto guild_bank = GetGuildBank(guild_id);
|
||||
// if (!guild_bank) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// if (area != GuildBankMainArea) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// auto item = &guild_bank->items.main_area[slot_id];
|
||||
// uint8 permissions = item->permissions;
|
||||
//
|
||||
// if (permissions == GuildBankBankerOnly) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
int32 GuildBankManager::NextFreeBankSlot(uint32 guild_id, uint32 area)
|
||||
{
|
||||
auto guild_bank = GetGuildBank(guild_id);
|
||||
@@ -1760,3 +1724,38 @@ void GuildBankManager::SendGuildBankItemUpdate(uint32 guild_id, int32 slot_id, u
|
||||
|
||||
entity_list.QueueClientsGuildBankItemUpdate(&gbius, guild_id);
|
||||
}
|
||||
|
||||
bool ZoneGuildManager::SetGuild(Client *client, uint32 guild_id, uint8 rank)
|
||||
{
|
||||
if (!client || rank > GUILD_MAX_RANK || !GetGuildByGuildID(guild_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rank <= GUILD_RANK_NONE) {
|
||||
rank = GUILD_RECRUIT;
|
||||
}
|
||||
|
||||
const uint32 current_guild_id = client->GuildID();
|
||||
if (current_guild_id == guild_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_guild_id != guild_id && current_guild_id != GUILD_NONE) {
|
||||
guild_mgr.RemoveMember(client->GuildID(), client->CharacterID(), std::string(client->GetCleanName()));
|
||||
}
|
||||
|
||||
client->SetGuildID(guild_id);
|
||||
client->SetGuildRank(rank);
|
||||
MemberAdd(
|
||||
guild_id,
|
||||
client->CharacterID(),
|
||||
client->GetLevel(),
|
||||
client->GetClass(),
|
||||
rank,
|
||||
client->GetZoneID(),
|
||||
client->GetName()
|
||||
);
|
||||
|
||||
client->SendGuildSpawnAppearance();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
void ListGuilds(Client *c, uint32 guild_id = 0) const;
|
||||
void DescribeGuild(Client *c, uint32 guild_id) const;
|
||||
bool IsActionABankAction(GuildAction action);
|
||||
bool SetGuild(Client *client, uint32 guild_id, uint8 rank);
|
||||
|
||||
uint8 *MakeGuildMembers(uint32 guild_id, const char* prefix_name, uint32& length);
|
||||
void SendToWorldMemberLevelUpdate(uint32 guild_id, uint32 level, std::string player_name);
|
||||
|
||||
+14
-1
@@ -276,6 +276,11 @@ void NPC::AddLootDrop(
|
||||
uint32 augment_six
|
||||
)
|
||||
{
|
||||
if (m_resumed_from_zone_suspend) {
|
||||
LogZoneState("NPC [{}] is resuming from zone suspend, skipping AddItem", GetCleanName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item2) {
|
||||
return;
|
||||
}
|
||||
@@ -370,6 +375,10 @@ void NPC::AddLootDrop(
|
||||
if (item2->Slots & slots) {
|
||||
if (equipment[i]) {
|
||||
compitem = database.GetItem(equipment[i]);
|
||||
if (!compitem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item2->AC > compitem->AC || (item2->AC == compitem->AC && item2->HP > compitem->HP)) {
|
||||
// item would be an upgrade
|
||||
// check if we're multi-slot, if yes then we have to keep
|
||||
@@ -380,6 +389,9 @@ void NPC::AddLootDrop(
|
||||
else {
|
||||
// Unequip old item
|
||||
auto *old_item = GetItem(i);
|
||||
if (!old_item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
old_item->equip_slot = EQ::invslot::SLOT_INVALID;
|
||||
|
||||
@@ -500,6 +512,7 @@ void NPC::AddLootDrop(
|
||||
parse->EventNPC(EVENT_LOOT_ADDED, this, nullptr, "", 0, &args);
|
||||
}
|
||||
|
||||
item->lootdrop_id = loot_drop.lootdrop_id;
|
||||
m_loot_items.push_back(item);
|
||||
|
||||
if (found) {
|
||||
@@ -671,7 +684,7 @@ LootItem *NPC::GetItem(int slot_id)
|
||||
end = m_loot_items.end();
|
||||
for (; cur != end; ++cur) {
|
||||
LootItem *item = *cur;
|
||||
if (item->equip_slot == slot_id) {
|
||||
if (item && item->equip_slot == slot_id) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
+32
-11
@@ -1199,16 +1199,6 @@ void Lua_Client::SetStartZone(int zone_id, float x, float y, float z) {
|
||||
self->SetStartZone(zone_id, x, y, z);
|
||||
}
|
||||
|
||||
void Lua_Client::KeyRingAdd(uint32 item) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->KeyRingAdd(item);
|
||||
}
|
||||
|
||||
bool Lua_Client::KeyRingCheck(uint32 item) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->KeyRingCheck(item);
|
||||
}
|
||||
|
||||
void Lua_Client::AddPVPPoints(uint32 points) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddPVPPoints(points);
|
||||
@@ -3548,6 +3538,34 @@ std::string Lua_Client::GetPotionBeltItemName(uint8 slot_id)
|
||||
return self->GetPotionBeltItemName(slot_id);
|
||||
}
|
||||
|
||||
bool Lua_Client::KeyRingAdd(uint32 item) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->KeyRingAdd(item);
|
||||
}
|
||||
|
||||
bool Lua_Client::KeyRingCheck(uint32 item) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->KeyRingCheck(item);
|
||||
}
|
||||
|
||||
bool Lua_Client::KeyRingClear()
|
||||
{
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->KeyRingClear();
|
||||
}
|
||||
|
||||
void Lua_Client::KeyRingList()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->KeyRingList();
|
||||
}
|
||||
|
||||
bool Lua_Client::KeyRingRemove(uint32 item_id)
|
||||
{
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->KeyRingRemove(item_id);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_client() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -3869,8 +3887,11 @@ luabind::scope lua_register_client() {
|
||||
.def("IsTaskActive", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskActive)
|
||||
.def("IsTaskActivityActive", (bool(Lua_Client::*)(int,int))&Lua_Client::IsTaskActivityActive)
|
||||
.def("IsTaskCompleted", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskCompleted)
|
||||
.def("KeyRingAdd", (void(Lua_Client::*)(uint32))&Lua_Client::KeyRingAdd)
|
||||
.def("KeyRingAdd", (bool(Lua_Client::*)(uint32))&Lua_Client::KeyRingAdd)
|
||||
.def("KeyRingCheck", (bool(Lua_Client::*)(uint32))&Lua_Client::KeyRingCheck)
|
||||
.def("KeyRingClear", (bool(Lua_Client::*)(void))&Lua_Client::KeyRingClear)
|
||||
.def("KeyRingList", (void(Lua_Client::*)(void))&Lua_Client::KeyRingList)
|
||||
.def("KeyRingRemove", (bool(Lua_Client::*)(uint32))&Lua_Client::KeyRingRemove)
|
||||
.def("Kick", (void(Lua_Client::*)(void))&Lua_Client::Kick)
|
||||
.def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines)
|
||||
.def("LearnRecipe", (void(Lua_Client::*)(uint32))&Lua_Client::LearnRecipe)
|
||||
|
||||
+5
-2
@@ -313,8 +313,6 @@ public:
|
||||
void SetStartZone(int zone_id, float x, float y);
|
||||
void SetStartZone(int zone_id, float x, float y, float z);
|
||||
void SetStartZone(int zone_id, float x, float y, float z, float heading);
|
||||
void KeyRingAdd(uint32 item);
|
||||
bool KeyRingCheck(uint32 item);
|
||||
void AddPVPPoints(uint32 points);
|
||||
void AddCrystals(uint32 radiant_count, uint32 ebon_count);
|
||||
void SetEbonCrystals(uint32 value);
|
||||
@@ -518,6 +516,11 @@ public:
|
||||
uint32 GetPotionBeltItemIcon(uint8 slot_id);
|
||||
uint32 GetPotionBeltItemID(uint8 slot_id);
|
||||
std::string GetPotionBeltItemName(uint8 slot_id);
|
||||
bool KeyRingAdd(uint32 item_id);
|
||||
bool KeyRingCheck(uint32 item_id);
|
||||
bool KeyRingClear();
|
||||
void KeyRingList();
|
||||
bool KeyRingRemove(uint32 item_id);
|
||||
|
||||
// account data buckets
|
||||
void SetAccountBucket(std::string bucket_name, std::string bucket_value);
|
||||
|
||||
+27
-1
@@ -468,7 +468,7 @@ void lua_set_sky(int sky) {
|
||||
}
|
||||
|
||||
void lua_set_guild(int guild_id, int rank) {
|
||||
quest_manager.setguild(guild_id, rank);
|
||||
quest_manager.SetGuild(guild_id, rank);
|
||||
}
|
||||
|
||||
void lua_create_guild(const char *name, const char *leader) {
|
||||
@@ -5642,6 +5642,31 @@ Lua_Zone lua_get_zone()
|
||||
return Lua_Zone(zone);
|
||||
}
|
||||
|
||||
bool lua_handin(luabind::adl::object handin_table)
|
||||
{
|
||||
std::map<std::string, uint32> handin_map;
|
||||
|
||||
for (luabind::iterator i(handin_table), end; i != end; i++) {
|
||||
std::string key;
|
||||
if (luabind::type(i.key()) == LUA_TSTRING) {
|
||||
key = luabind::object_cast<std::string>(i.key());
|
||||
}
|
||||
else if (luabind::type(i.key()) == LUA_TNUMBER) {
|
||||
key = fmt::format("{}", luabind::object_cast<int>(i.key()));
|
||||
}
|
||||
else {
|
||||
LogError("Handin key type [{}] not supported", luabind::type(i.key()));
|
||||
}
|
||||
|
||||
if (!key.empty()) {
|
||||
handin_map[key] = luabind::object_cast<uint32>(handin_table[i.key()]);
|
||||
LogNpcHandinDetail("Handin key [{}] value [{}]", key, handin_map[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return quest_manager.handin(handin_map);
|
||||
}
|
||||
|
||||
#define LuaCreateNPCParse(name, c_type, default_value) do { \
|
||||
cur = table[#name]; \
|
||||
if(luabind::type(cur) != LUA_TNIL) { \
|
||||
@@ -6450,6 +6475,7 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("spawn_circle", &lua_spawn_circle),
|
||||
luabind::def("spawn_grid", &lua_spawn_grid),
|
||||
luabind::def("get_zone", &lua_get_zone),
|
||||
luabind::def("handin", &lua_handin),
|
||||
/*
|
||||
Cross Zone
|
||||
*/
|
||||
|
||||
@@ -939,6 +939,12 @@ Lua_Spawn Lua_NPC::GetSpawn(lua_State* L)
|
||||
return Lua_Spawn(self->GetSpawn());
|
||||
}
|
||||
|
||||
bool Lua_NPC::IsResumedFromZoneSuspend()
|
||||
{
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsResumedFromZoneSuspend();
|
||||
}
|
||||
|
||||
luabind::scope lua_register_npc() {
|
||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -1040,6 +1046,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("IsOnHatelist", (bool(Lua_NPC::*)(Lua_Mob))&Lua_NPC::IsOnHatelist)
|
||||
.def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget)
|
||||
.def("IsRareSpawn", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRareSpawn)
|
||||
.def("IsResumedFromZoneSuspend",(bool(Lua_NPC::*)(void))&Lua_NPC::IsResumedFromZoneSuspend)
|
||||
.def("IsTaunting", (bool(Lua_NPC::*)(void))&Lua_NPC::IsTaunting)
|
||||
.def("IsUnderwaterOnly", (bool(Lua_NPC::*)(void))&Lua_NPC::IsUnderwaterOnly)
|
||||
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
|
||||
|
||||
@@ -198,6 +198,7 @@ public:
|
||||
);
|
||||
void ReturnHandinItems(Lua_Client c);
|
||||
Lua_Spawn GetSpawn(lua_State* L);
|
||||
bool IsResumedFromZoneSuspend();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+29
-21
@@ -631,7 +631,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (zone) {
|
||||
if (!zone->Process()) {
|
||||
Zone::Shutdown();
|
||||
zone->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,7 +668,7 @@ int main(int argc, char **argv)
|
||||
safe_delete(Config);
|
||||
|
||||
if (zone != 0) {
|
||||
Zone::Shutdown(true);
|
||||
zone->Shutdown(true);
|
||||
}
|
||||
//Fix for Linux world server problem.
|
||||
safe_delete(task_manager);
|
||||
@@ -687,7 +687,7 @@ int main(int argc, char **argv)
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
Zone::Shutdown(true);
|
||||
zone->Shutdown(true);
|
||||
LogInfo("Shutting down...");
|
||||
LogSys.CloseFileLogs();
|
||||
EQ::EventLoop::Get().Shutdown();
|
||||
@@ -731,37 +731,45 @@ void UpdateWindowTitle(char *iNewTitle)
|
||||
|
||||
bool CheckForCompatibleQuestPlugins()
|
||||
{
|
||||
const std::vector<std::string>& directories = { "lua_modules", "plugins" };
|
||||
const std::vector<std::pair<std::string, bool *>> directories = {
|
||||
{"lua_modules", nullptr},
|
||||
{"plugins", nullptr}
|
||||
};
|
||||
|
||||
bool lua_found = false;
|
||||
bool perl_found = false;
|
||||
|
||||
for (const auto& directory : directories) {
|
||||
for (const auto& file : fs::directory_iterator(path.GetServerPath() + "/" + directory)) {
|
||||
if (file.is_regular_file()) {
|
||||
auto f = file.path().string();
|
||||
if (File::Exists(f)) {
|
||||
auto r = File::GetContents(std::filesystem::path{ f }.string());
|
||||
if (Strings::Contains(r.contents, "CheckHandin")) {
|
||||
if (Strings::EqualFold(directory, "lua_modules")) {
|
||||
lua_found = true;
|
||||
} else if (Strings::EqualFold(directory, "plugins")) {
|
||||
perl_found = true;
|
||||
}
|
||||
try {
|
||||
for (const auto &[directory, flag]: directories) {
|
||||
std::string dir_path = path.GetServerPath() + "/" + directory;
|
||||
if (!File::Exists(dir_path)) { continue; }
|
||||
|
||||
if (lua_found && perl_found) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const auto &file: fs::directory_iterator(dir_path)) {
|
||||
if (!file.is_regular_file()) { continue; }
|
||||
|
||||
std::string file_path = file.path().string();
|
||||
if (!File::Exists(file_path)) { continue; }
|
||||
|
||||
auto r = File::GetContents(file_path);
|
||||
if (!Strings::Contains(r.contents, "CheckHandin")) { continue; }
|
||||
|
||||
if (directory == "lua_modules") {
|
||||
lua_found = true;
|
||||
}
|
||||
else {
|
||||
perl_found = true;
|
||||
}
|
||||
|
||||
if (lua_found && perl_found) { return true; }
|
||||
}
|
||||
}
|
||||
} catch (const fs::filesystem_error &ex) {
|
||||
LogError("Failed to check for compatible quest plugins: {}", ex.what());
|
||||
}
|
||||
|
||||
if (!lua_found) {
|
||||
LogError("Failed to find CheckHandin in lua_modules");
|
||||
}
|
||||
|
||||
if (!perl_found) {
|
||||
LogError("Failed to find CheckHandin in plugins");
|
||||
}
|
||||
|
||||
+3
-3
@@ -531,6 +531,9 @@ Mob::Mob(
|
||||
|
||||
Mob::~Mob()
|
||||
{
|
||||
entity_list.RemoveMobFromCloseLists(this);
|
||||
m_close_mobs.clear();
|
||||
|
||||
quest_manager.stopalltimers(this);
|
||||
|
||||
mMovementManager->RemoveMob(this);
|
||||
@@ -570,11 +573,8 @@ Mob::~Mob()
|
||||
entity_list.UnMarkNPC(GetID());
|
||||
UninitializeBuffSlots();
|
||||
|
||||
entity_list.RemoveMobFromCloseLists(this);
|
||||
entity_list.RemoveAuraFromMobs(this);
|
||||
|
||||
m_close_mobs.clear();
|
||||
|
||||
ClearDataBucketCache();
|
||||
|
||||
LeaveHealRotationTargetPool();
|
||||
|
||||
+57
-3
@@ -62,6 +62,7 @@
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#endif
|
||||
|
||||
extern Zone* zone;
|
||||
@@ -131,6 +132,9 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
||||
),
|
||||
attacked_timer(CombatEventTimer_expire),
|
||||
swarm_timer(100),
|
||||
m_corpse_queue_timer(1000),
|
||||
m_corpse_queue_shutoff_timer(30000),
|
||||
m_resumed_from_zone_suspend_shutoff_timer(30000),
|
||||
classattack_timer(1000),
|
||||
monkattack_timer(1000),
|
||||
knightattack_timer(1000),
|
||||
@@ -618,7 +622,49 @@ bool NPC::Process()
|
||||
}
|
||||
}
|
||||
|
||||
// zone state corpse creation timer
|
||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||
// creates a corpse if the NPC is queued for corpse creation
|
||||
if (m_corpse_queue_timer.Check()) {
|
||||
if (IsQueuedForCorpse()) {
|
||||
auto decay_timer = m_corpse_decay_time;
|
||||
uint16 corpse_id = GetID();
|
||||
Death(this, GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
||||
if (c) {
|
||||
c->UnLock();
|
||||
c->SetDecayTimer(decay_timer);
|
||||
}
|
||||
}
|
||||
m_corpse_queue_timer.Disable();
|
||||
m_corpse_queue_shutoff_timer.Disable();
|
||||
}
|
||||
|
||||
// shuts off the corpse queue timer if it is still running
|
||||
if (m_corpse_queue_shutoff_timer.Check()) {
|
||||
m_corpse_queue_timer.Disable();
|
||||
m_corpse_queue_shutoff_timer.Disable();
|
||||
}
|
||||
|
||||
// shuts off the temporary spawn protected state of the NPC
|
||||
if (m_resumed_from_zone_suspend_shutoff_timer.Check()) {
|
||||
m_resumed_from_zone_suspend_shutoff_timer.Disable();
|
||||
SetResumedFromZoneSuspend(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (tic_timer.Check()) {
|
||||
if (RuleB(Zone, StateSavingOnShutdown) && IsQueuedForCorpse()) {
|
||||
auto decay_timer = m_corpse_decay_time;
|
||||
uint16 corpse_id = GetID();
|
||||
Death(this, GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
||||
if (c) {
|
||||
c->UnLock();
|
||||
c->SetDecayTimer(decay_timer);
|
||||
}
|
||||
}
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TICK)) {
|
||||
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
||||
}
|
||||
@@ -4269,7 +4315,7 @@ bool NPC::CanPetTakeItem(const EQ::ItemInstance *inst)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsPetOwnerClient() && !IsCharmedPet()) {
|
||||
if (!IsPetOwnerOfClientBot() && !IsCharmedPet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4351,6 +4397,10 @@ bool NPC::CheckHandin(
|
||||
h = m_hand_in;
|
||||
}
|
||||
|
||||
if (IsMultiQuestEnabled()) {
|
||||
LogNpcHandin("{} Multi-Quest hand-in enabled", log_handin_prefix);
|
||||
}
|
||||
|
||||
std::vector<std::pair<const std::map<std::string, uint32>&, Handin&>> datasets = {};
|
||||
|
||||
// if we've already started the hand-in process, we don't want to re-process the hand-in data
|
||||
@@ -4432,7 +4482,7 @@ bool NPC::CheckHandin(
|
||||
|
||||
// multi-quest
|
||||
if (IsMultiQuestEnabled()) {
|
||||
for (auto &h_item: h.items) {
|
||||
for (auto &h_item: m_hand_in.items) {
|
||||
for (const auto &r_item: r.items) {
|
||||
if (h_item.item_id == r_item.item_id && h_item.count == r_item.count) {
|
||||
h_item.is_multiquest_item = true;
|
||||
@@ -4777,7 +4827,11 @@ NPC::Handin NPC::ReturnHandinItems(Client *c)
|
||||
}
|
||||
|
||||
c->PushItemOnCursor(*i.item, true);
|
||||
LogNpcHandin("Hand-in failed, returning item [{}]", i.item->GetItem()->Name);
|
||||
LogNpcHandin(
|
||||
"Hand-in failed, returning item [{}] i.is_multiquest_item [{}]",
|
||||
i.item->GetItem()->Name,
|
||||
i.is_multiquest_item
|
||||
);
|
||||
|
||||
returned_handin = true;
|
||||
return true; // Mark this item for removal
|
||||
|
||||
+19
@@ -601,6 +601,13 @@ public:
|
||||
bool HasProcessedHandinReturn() { return m_has_processed_handin_return; }
|
||||
bool HandinStarted() { return m_handin_started; }
|
||||
|
||||
// zone state save
|
||||
inline void SetQueuedToCorpse() { m_queued_for_corpse = true; }
|
||||
inline bool IsQueuedForCorpse() { return m_queued_for_corpse; }
|
||||
inline uint32_t SetCorpseDecayTime(uint32_t decay_time) { return m_corpse_decay_time = decay_time; }
|
||||
inline void SetResumedFromZoneSuspend(bool state = true) { m_resumed_from_zone_suspend = state; }
|
||||
inline bool IsResumedFromZoneSuspend() { return m_resumed_from_zone_suspend; }
|
||||
|
||||
protected:
|
||||
|
||||
void HandleRoambox();
|
||||
@@ -622,6 +629,18 @@ protected:
|
||||
uint32 m_loot_platinum;
|
||||
LootItems m_loot_items;
|
||||
|
||||
// zone state
|
||||
bool m_resumed_from_zone_suspend = false;
|
||||
bool m_queued_for_corpse = false; // this is to check for corpse creation on zone state restore
|
||||
uint32_t m_corpse_decay_time = 0; // decay time set on zone state restore
|
||||
Timer m_corpse_queue_timer = {}; // this is to check for corpse creation on zone state restore
|
||||
Timer m_corpse_queue_shutoff_timer = {};
|
||||
|
||||
// this is a 30-second timer that protects a NPC from having double assignment of loot
|
||||
// this is to prevent a player from killing a NPC and then zoning out and back in to get loot again
|
||||
// if loot was to be assigned via script again, this protects double assignment for 30 seconds
|
||||
Timer m_resumed_from_zone_suspend_shutoff_timer = {};
|
||||
|
||||
std::list<NpcFactionEntriesRepository::NpcFactionEntries> faction_list;
|
||||
|
||||
int32 npc_faction_id;
|
||||
|
||||
+28
-10
@@ -1142,16 +1142,6 @@ void Perl_Client_SetStartZone(Client* self, uint32 zone_id, float x, float y, fl
|
||||
self->SetStartZone(zone_id, x, y, z, heading);
|
||||
}
|
||||
|
||||
void Perl_Client_KeyRingAdd(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items
|
||||
{
|
||||
self->KeyRingAdd(item_id);
|
||||
}
|
||||
|
||||
bool Perl_Client_KeyRingCheck(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items
|
||||
{
|
||||
return self->KeyRingCheck(item_id);
|
||||
}
|
||||
|
||||
void Perl_Client_AddPVPPoints(Client* self, uint32 points) // @categories Currency and Points
|
||||
{
|
||||
self->AddPVPPoints(points);
|
||||
@@ -3306,6 +3296,31 @@ std::string Perl_Client_GetPotionBeltItemName(Client* self, uint8 slot_id)
|
||||
return self->GetPotionBeltItemName(slot_id);
|
||||
}
|
||||
|
||||
bool Perl_Client_KeyRingAdd(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items
|
||||
{
|
||||
return self->KeyRingAdd(item_id);
|
||||
}
|
||||
|
||||
bool Perl_Client_KeyRingCheck(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items
|
||||
{
|
||||
return self->KeyRingCheck(item_id);
|
||||
}
|
||||
|
||||
bool Perl_Client_KeyRingClear(Client* self)
|
||||
{
|
||||
return self->KeyRingClear();
|
||||
}
|
||||
|
||||
void Perl_Client_KeyRingList(Client* self)
|
||||
{
|
||||
self->KeyRingList();
|
||||
}
|
||||
|
||||
bool Perl_Client_KeyRingRemove(Client* self, uint32 item_id)
|
||||
{
|
||||
return self->KeyRingRemove(item_id);
|
||||
}
|
||||
|
||||
void perl_register_client()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -3630,6 +3645,9 @@ void perl_register_client()
|
||||
package.add("IsTaskCompleted", &Perl_Client_IsTaskCompleted);
|
||||
package.add("KeyRingAdd", &Perl_Client_KeyRingAdd);
|
||||
package.add("KeyRingCheck", &Perl_Client_KeyRingCheck);
|
||||
package.add("KeyRingClear", &Perl_Client_KeyRingClear);
|
||||
package.add("KeyRingList", &Perl_Client_KeyRingList);
|
||||
package.add("KeyRingRemove", &Perl_Client_KeyRingRemove);
|
||||
package.add("Kick", &Perl_Client_Kick);
|
||||
package.add("LearnDisciplines", &Perl_Client_LearnDisciplines);
|
||||
package.add("LearnRecipe", &Perl_Client_LearnRecipe);
|
||||
|
||||
@@ -806,6 +806,11 @@ void Perl_NPC_MultiQuestEnable(NPC* self)
|
||||
self->MultiQuestEnable();
|
||||
}
|
||||
|
||||
bool Perl_NPC_IsResumedFromZoneSuspend(NPC* self)
|
||||
{
|
||||
return self->IsResumedFromZoneSuspend();
|
||||
}
|
||||
|
||||
bool Perl_NPC_CheckHandin(
|
||||
NPC* self,
|
||||
Client* c,
|
||||
@@ -983,6 +988,7 @@ void perl_register_npc()
|
||||
package.add("IsOnHatelist", &Perl_NPC_IsOnHatelist);
|
||||
package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget);
|
||||
package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn);
|
||||
package.add("IsResumedFromZoneSuspend", &Perl_NPC_IsResumedFromZoneSuspend);
|
||||
package.add("IsTaunting", &Perl_NPC_IsTaunting);
|
||||
package.add("IsUnderwaterOnly", (bool(*)(NPC*))&Perl_NPC_IsUnderwaterOnly);
|
||||
package.add("MerchantCloseShop", &Perl_NPC_MerchantCloseShop);
|
||||
|
||||
+18
-3
@@ -1623,10 +1623,10 @@ void QuestManager::setsky(uint8 new_sky) {
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void QuestManager::setguild(uint32 new_guild_id, uint8 new_rank) {
|
||||
void QuestManager::SetGuild(uint32 new_guild_id, uint8 new_rank) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (initiator) {
|
||||
guild_mgr.SetGuild(initiator->CharacterID(), new_guild_id, new_rank);
|
||||
guild_mgr.SetGuild(initiator, new_guild_id, new_rank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1681,7 +1681,7 @@ void QuestManager::CreateGuild(const char *guild_name, const char *leader) {
|
||||
gid
|
||||
).c_str()
|
||||
);
|
||||
if (!guild_mgr.SetGuild(character_id, gid, GUILD_LEADER)) {
|
||||
if (!guild_mgr.SetGuild(initiator, gid, GUILD_LEADER)) {
|
||||
worldserver.SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
@@ -4606,3 +4606,18 @@ void QuestManager::SpawnGrid(uint32 npc_id, glm::vec4 position, float spacing, u
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QuestManager::handin(std::map<std::string, uint32> required) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner || !initiator) {
|
||||
LogQuests("QuestManager::handin called with nullptr owner. Probably syntax error in quest file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (owner && !owner->IsNPC()) {
|
||||
LogQuests("QuestManager::handin called with non-NPC owner. Probably syntax error in quest file");
|
||||
return false;
|
||||
}
|
||||
|
||||
return owner->CastToNPC()->CheckHandin(initiator, {}, required, {});
|
||||
}
|
||||
|
||||
+2
-1
@@ -152,7 +152,7 @@ public:
|
||||
void faction(int faction_id, int faction_value, int temp);
|
||||
void rewardfaction(int faction_id, int faction_value);
|
||||
void setsky(uint8 new_sky);
|
||||
void setguild(uint32 new_guild_id, uint8 new_rank);
|
||||
void SetGuild(uint32 new_guild_id, uint8 new_rank);
|
||||
void CreateGuild(const char *guild_name, const char *leader);
|
||||
void settime(uint8 new_hour, uint8 new_min, bool update_world = true);
|
||||
void itemlink(int item_id);
|
||||
@@ -376,6 +376,7 @@ public:
|
||||
bool botquest();
|
||||
bool createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender);
|
||||
|
||||
bool handin(std::map<std::string, uint32> required);
|
||||
|
||||
private:
|
||||
std::stack<running_quest> quests_running_;
|
||||
|
||||
+18
-7
@@ -16,6 +16,7 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/strings.h"
|
||||
|
||||
@@ -33,6 +34,7 @@
|
||||
#include "../common/repositories/spawn2_repository.h"
|
||||
#include "../common/repositories/spawn2_disabled_repository.h"
|
||||
#include "../common/repositories/respawn_times_repository.h"
|
||||
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern Zone* zone;
|
||||
@@ -85,9 +87,9 @@ Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id,
|
||||
x = in_x;
|
||||
y = in_y;
|
||||
z = in_z;
|
||||
heading = in_heading;
|
||||
respawn_ = respawn;
|
||||
variance_ = variance;
|
||||
heading = in_heading;
|
||||
m_respawn_time = respawn;
|
||||
variance_ = variance;
|
||||
grid_ = grid;
|
||||
path_when_zone_idle = in_path_when_zone_idle;
|
||||
condition_id = in_cond_id;
|
||||
@@ -95,6 +97,7 @@ Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id,
|
||||
npcthis = nullptr;
|
||||
enabled = in_enabled;
|
||||
this->anim = anim;
|
||||
currentnpcid = 0;
|
||||
|
||||
if(timeleft == 0xFFFFFFFF) {
|
||||
//special disable timeleft
|
||||
@@ -115,7 +118,7 @@ Spawn2::~Spawn2()
|
||||
|
||||
uint32 Spawn2::resetTimer()
|
||||
{
|
||||
uint32 rspawn = respawn_ * 1000;
|
||||
uint32 rspawn = m_respawn_time * 1000;
|
||||
|
||||
if (variance_ != 0) {
|
||||
int var_over_2 = (variance_ * 1000) / 2;
|
||||
@@ -150,12 +153,12 @@ uint32 Spawn2::despawnTimer(uint32 despawn_timer)
|
||||
bool Spawn2::Process() {
|
||||
IsDespawned = false;
|
||||
|
||||
if (!Enabled())
|
||||
if (!Enabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//grab our spawn group
|
||||
SpawnGroup *spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
|
||||
|
||||
if (NPCPointerValid() && (spawn_group && spawn_group->despawn == 0 || condition_id != 0)) {
|
||||
return true;
|
||||
}
|
||||
@@ -195,7 +198,7 @@ bool Spawn2::Process() {
|
||||
}
|
||||
|
||||
//have the spawn group pick an NPC for us
|
||||
uint32 npcid = spawn_group->GetNPCType(condition_value);
|
||||
uint32 npcid = currentnpcid && currentnpcid > 0 ? currentnpcid : spawn_group->GetNPCType(condition_value);
|
||||
if (npcid == 0) {
|
||||
LogSpawns("Spawn2 [{}]: Spawn group [{}] did not yeild an NPC! not spawning", spawn2_id, spawngroup_id_);
|
||||
|
||||
@@ -267,10 +270,12 @@ bool Spawn2::Process() {
|
||||
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water);
|
||||
|
||||
npcthis = npc;
|
||||
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot()) {
|
||||
npc->CheckGlobalLootTables();
|
||||
}
|
||||
|
||||
npc->SetSpawnGroupId(spawngroup_id_);
|
||||
npc->SaveGuardPointAnim(anim);
|
||||
npc->SetAppearance((EmuAppearance) anim);
|
||||
@@ -500,6 +505,12 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spa
|
||||
|
||||
NPC::SpawnZoneController();
|
||||
|
||||
if (RuleB(Zone, StateSavingOnShutdown) && zone->LoadZoneState(spawn_times, disabled_spawns)) {
|
||||
LogZoneState("Loaded zone state for zone [{}] instance_id [{}]", zone_name, zone->GetInstanceID());
|
||||
return true;
|
||||
}
|
||||
|
||||
// normal spawn2 loading
|
||||
for (auto &s: spawns) {
|
||||
uint32 spawn_time_left = 0;
|
||||
if (spawn_times.count(s.id) != 0) {
|
||||
|
||||
+11
-5
@@ -55,10 +55,10 @@ public:
|
||||
float GetZ() { return z; }
|
||||
float GetHeading() { return heading; }
|
||||
bool PathWhenZoneIdle() { return path_when_zone_idle; }
|
||||
void SetRespawnTimer(uint32 newrespawntime) { respawn_ = newrespawntime; };
|
||||
void SetRespawnTimer(uint32 newrespawntime) { m_respawn_time = newrespawntime; };
|
||||
void SetVariance(uint32 newvariance) { variance_ = newvariance; }
|
||||
const uint32 GetVariance() const { return variance_; }
|
||||
uint32 RespawnTimer() { return respawn_; }
|
||||
uint32 RespawnTimer() { return m_respawn_time; }
|
||||
uint32 SpawnGroupID() { return spawngroup_id_; }
|
||||
uint32 CurrentNPCID() { return currentnpcid; }
|
||||
void SetCurrentNPCID(uint32 nid) { currentnpcid = nid; }
|
||||
@@ -69,13 +69,19 @@ public:
|
||||
void SetNPCPointerNull() { npcthis = nullptr; }
|
||||
Timer GetTimer() { return timer; }
|
||||
void SetTimer(uint32 duration) { timer.Start(duration); }
|
||||
uint32 GetKillCount() { return killcount; }
|
||||
uint32 GetKillCount() { return killcount; }
|
||||
uint32 GetGrid() const { return grid_; }
|
||||
bool GetPathWhenZoneIdle() const { return path_when_zone_idle; }
|
||||
int16 GetConditionMinValue() const { return condition_min_value; }
|
||||
int16 GetAnimation () { return anim; }
|
||||
inline NPC *GetNPC() const { return npcthis; }
|
||||
|
||||
protected:
|
||||
friend class Zone;
|
||||
Timer timer;
|
||||
private:
|
||||
uint32 spawn2_id;
|
||||
uint32 respawn_;
|
||||
uint32 spawn2_id;
|
||||
uint32 m_respawn_time;
|
||||
uint32 resetTimer();
|
||||
uint32 despawnTimer(uint32 despawn_timer);
|
||||
|
||||
|
||||
@@ -1243,6 +1243,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_SummonItemIntoBag:
|
||||
{
|
||||
const EQ::ItemData *item = database.GetItem(spell.base_value[i]);
|
||||
if (!item) {
|
||||
Message(Chat::Red, "Unable to summon item %d. Item not found.", spell.base_value[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
const char *itemname = item ? item->Name : "*Unknown Item*";
|
||||
snprintf(effect_desc, _EDLEN, "Summon Item In Bag: %s (id %d)", itemname, spell.base_value[i]);
|
||||
|
||||
@@ -3135,6 +3135,17 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& always_stack_spells = RuleS(Spells, AlwaysStackSpells);
|
||||
if (spellid1 != spellid2 && !always_stack_spells.empty()) {
|
||||
const auto& v = Strings::Split(always_stack_spells, ",");
|
||||
if (Strings::Contains(v, std::to_string(spellid1))) {
|
||||
return 0;
|
||||
}
|
||||
if (Strings::Contains(v, std::to_string(spellid2))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
One of these is a bard song and one isn't and they're both beneficial so they should stack.
|
||||
*/
|
||||
|
||||
@@ -952,6 +952,8 @@ int ClientTaskState::IncrementDoneCount(
|
||||
|
||||
client->CancelTask(task_index, task_data->type);
|
||||
}
|
||||
|
||||
client->LoadClientSharedCompletedTasks();
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1561,7 +1563,7 @@ int ClientTaskState::TaskTimeLeft(int task_id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ClientTaskState::IsTaskCompleted(int task_id)
|
||||
bool ClientTaskState::IsTaskCompleted(int task_id, Client *c)
|
||||
{
|
||||
if (!RuleB(TaskSystem, RecordCompletedTasks)) {
|
||||
return false;
|
||||
@@ -1574,6 +1576,14 @@ bool ClientTaskState::IsTaskCompleted(int task_id)
|
||||
}
|
||||
}
|
||||
|
||||
if (c) {
|
||||
for (auto &e: c->GetCompletedSharedTasks()) {
|
||||
if (e == task_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
void AcceptNewTask(Client *client, int task_id, int npc_type_id, time_t accept_time, bool enforce_level_requirement = false);
|
||||
void FailTask(Client *client, int task_id);
|
||||
int TaskTimeLeft(int task_id);
|
||||
bool IsTaskCompleted(int task_id);
|
||||
bool IsTaskCompleted(int task_id, Client *c = nullptr);
|
||||
bool AreTasksCompleted(const std::vector<int>& task_ids);
|
||||
bool IsTaskActive(int task_id);
|
||||
bool IsTaskActivityActive(int task_id, int activity_id);
|
||||
|
||||
+2
-1
@@ -15,8 +15,9 @@ extern QueryServ *QServ;
|
||||
void Client::LoadClientTaskState()
|
||||
{
|
||||
if (RuleB(TaskSystem, EnableTaskSystem) && task_manager) {
|
||||
safe_delete(task_state);
|
||||
LoadClientSharedCompletedTasks();
|
||||
|
||||
safe_delete(task_state);
|
||||
task_state = new ClientTaskState();
|
||||
if (!task_manager->LoadClientState(this, task_state)) {
|
||||
safe_delete(task_state);
|
||||
|
||||
+8
-8
@@ -57,34 +57,34 @@ EQApplicationPacket* TitleManager::MakeTitlesPacket(Client* c)
|
||||
return outapp;
|
||||
}
|
||||
|
||||
std::string TitleManager::GetPrefix(int title_set)
|
||||
std::string TitleManager::GetPrefix(int title_id)
|
||||
{
|
||||
if (!title_set) {
|
||||
if (!title_id) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto e = std::find_if(
|
||||
titles.begin(),
|
||||
titles.end(),
|
||||
[title_set](const auto& t) {
|
||||
return t.title_set == title_set;
|
||||
[title_id](const auto& t) {
|
||||
return t.id == title_id;
|
||||
}
|
||||
);
|
||||
|
||||
return e != titles.end() ? e->prefix : "";
|
||||
}
|
||||
|
||||
std::string TitleManager::GetSuffix(int title_set)
|
||||
std::string TitleManager::GetSuffix(int title_id)
|
||||
{
|
||||
if (!title_set) {
|
||||
if (!title_id) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto e = std::find_if(
|
||||
titles.begin(),
|
||||
titles.end(),
|
||||
[title_set](const auto& t) {
|
||||
return t.title_set == title_set;
|
||||
[title_id](const auto& t) {
|
||||
return t.id == title_id;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ public:
|
||||
bool LoadTitles();
|
||||
|
||||
EQApplicationPacket* MakeTitlesPacket(Client* c);
|
||||
std::string GetPrefix(int title_set);
|
||||
std::string GetSuffix(int title_set);
|
||||
std::string GetPrefix(int title_id);
|
||||
std::string GetSuffix(int title_id);
|
||||
std::vector<TitlesRepository::Titles> GetEligibleTitles(Client* c);
|
||||
bool IsNewAATitleAvailable(int aa_points, int class_id);
|
||||
bool IsNewTradeSkillTitleAvailable(int t, int skill_value);
|
||||
|
||||
+23
-3
@@ -551,7 +551,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
|
||||
auto with = tradingWith->CastToNPC();
|
||||
const EQ::ItemData *item = inst->GetItem();
|
||||
const bool is_pet = with->IsPetOwnerClient() || with->IsCharmedPet();
|
||||
const bool is_pet = with->IsPetOwnerOfClientBot() || with->IsCharmedPet();
|
||||
if (is_pet && with->CanPetTakeItem(inst)) {
|
||||
// pets need to look inside bags and try to equip items found there
|
||||
if (item->IsClassBag() && item->BagSlots > 0) {
|
||||
@@ -618,16 +618,36 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
item_list.emplace_back(inst);
|
||||
}
|
||||
|
||||
auto handin_npc = tradingWith->CastToNPC();
|
||||
|
||||
m_external_handin_money_returned = {};
|
||||
m_external_handin_items_returned = {};
|
||||
bool has_aggro = tradingWith->CheckAggro(this);
|
||||
if (parse->HasQuestSub(tradingWith->GetNPCTypeID(), EVENT_TRADE) && !has_aggro) {
|
||||
// This CheckHandin call enables eq.handin and quest::handin to recognize the hand-in context.
|
||||
// It initializes the first hand-in bucket, which is then reused for the EVENT_TRADE subroutine.
|
||||
std::map<std::string, uint32> handin = {
|
||||
{"copper", trade->cp},
|
||||
{"silver", trade->sp},
|
||||
{"gold", trade->gp},
|
||||
{"platinum", trade->pp}
|
||||
};
|
||||
|
||||
for (EQ::ItemInstance *inst: items) {
|
||||
if (!inst || !inst->GetItem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string item_id = fmt::format("{}", inst->GetItem()->ID);
|
||||
handin[item_id] += inst->GetCharges();
|
||||
}
|
||||
|
||||
handin_npc->CheckHandin(this, handin, {}, items);
|
||||
|
||||
parse->EventNPC(EVENT_TRADE, tradingWith->CastToNPC(), this, "", 0, &item_list);
|
||||
LogNpcHandinDetail("EVENT_TRADE triggered for NPC [{}]", tradingWith->GetNPCTypeID());
|
||||
}
|
||||
|
||||
auto handin_npc = tradingWith->CastToNPC();
|
||||
|
||||
// this is a catch-all return for items that weren't consumed by the EVENT_TRADE subroutine
|
||||
// it's possible we have a quest NPC that doesn't have an EVENT_TRADE subroutine
|
||||
// we can't double fire the ReturnHandinItems() event, so we need to check if it's already been processed from EVENT_TRADE
|
||||
|
||||
+10
-13
@@ -591,7 +591,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
|
||||
auto *s = (ServerZoneStateChange_Struct *) pack->pBuffer;
|
||||
LogInfo("Zone shutdown by {}.", s->admin_name);
|
||||
Zone::Shutdown();
|
||||
zone->Shutdown();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4500,13 +4500,6 @@ void WorldServer::QueueReload(ServerReload::Request r)
|
||||
m_reload_mutex.lock();
|
||||
int64_t reload_at = r.reload_at_unix - std::time(nullptr);
|
||||
|
||||
// If the reload is set to happen now, process it immediately versus queuing it
|
||||
if (reload_at <= 0) {
|
||||
ProcessReload(r);
|
||||
m_reload_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Queuing reload for [{}] ({}) to reload in [{}]",
|
||||
ServerReload::GetName(r.type),
|
||||
@@ -4668,16 +4661,20 @@ void WorldServer::ProcessReload(const ServerReload::Request& request)
|
||||
break;
|
||||
|
||||
case ServerReload::Type::WorldRepop:
|
||||
entity_list.ClearAreas();
|
||||
parse->ReloadQuests();
|
||||
zone->Repop();
|
||||
if (zone && zone->IsLoaded()) {
|
||||
entity_list.ClearAreas();
|
||||
zone->Repop();
|
||||
}
|
||||
break;
|
||||
|
||||
case ServerReload::Type::WorldWithRespawn:
|
||||
entity_list.ClearAreas();
|
||||
parse->ReloadQuests();
|
||||
zone->Repop();
|
||||
zone->ClearSpawnTimers();
|
||||
if (zone && zone->IsLoaded()) {
|
||||
entity_list.ClearAreas();
|
||||
zone->Repop();
|
||||
zone->ClearSpawnTimers();
|
||||
}
|
||||
break;
|
||||
|
||||
case ServerReload::Type::ZonePoints:
|
||||
|
||||
+39
-22
@@ -64,6 +64,7 @@
|
||||
#include "../common/repositories/ldon_trap_templates_repository.h"
|
||||
#include "../common/repositories/respawn_times_repository.h"
|
||||
#include "../common/repositories/npc_emotes_repository.h"
|
||||
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||
#include "../common/serverinfo.h"
|
||||
#include "../common/repositories/merc_stance_entries_repository.h"
|
||||
#include "../common/repositories/alternate_currency_repository.h"
|
||||
@@ -880,57 +881,66 @@ void Zone::Shutdown(bool quiet)
|
||||
}
|
||||
|
||||
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Zone, zone->GetZoneID(), zone->GetInstanceID());
|
||||
// save and kick all clients
|
||||
for (auto c : entity_list.GetClientList()) {
|
||||
c.second->Save();
|
||||
c.second->WorldKick();
|
||||
}
|
||||
|
||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||
SaveZoneState();
|
||||
}
|
||||
|
||||
entity_list.StopMobAI();
|
||||
|
||||
std::map<uint32, NPCType *>::iterator itr;
|
||||
while (!zone->npctable.empty()) {
|
||||
itr = zone->npctable.begin();
|
||||
while (!npctable.empty()) {
|
||||
itr = npctable.begin();
|
||||
delete itr->second;
|
||||
itr->second = nullptr;
|
||||
zone->npctable.erase(itr);
|
||||
npctable.erase(itr);
|
||||
}
|
||||
|
||||
while (!zone->merctable.empty()) {
|
||||
itr = zone->merctable.begin();
|
||||
while (!merctable.empty()) {
|
||||
itr = merctable.begin();
|
||||
delete itr->second;
|
||||
itr->second = nullptr;
|
||||
zone->merctable.erase(itr);
|
||||
merctable.erase(itr);
|
||||
}
|
||||
|
||||
zone->adventure_entry_list_flavor.clear();
|
||||
adventure_entry_list_flavor.clear();
|
||||
|
||||
std::map<uint32, LDoNTrapTemplate *>::iterator itr4;
|
||||
while (!zone->ldon_trap_list.empty()) {
|
||||
itr4 = zone->ldon_trap_list.begin();
|
||||
while (!ldon_trap_list.empty()) {
|
||||
itr4 = ldon_trap_list.begin();
|
||||
delete itr4->second;
|
||||
itr4->second = nullptr;
|
||||
zone->ldon_trap_list.erase(itr4);
|
||||
ldon_trap_list.erase(itr4);
|
||||
}
|
||||
zone->ldon_trap_entry_list.clear();
|
||||
ldon_trap_entry_list.clear();
|
||||
|
||||
LogInfo(
|
||||
"Zone [{}] zone_id [{}] version [{}] instance_id [{}]",
|
||||
zone->GetShortName(),
|
||||
zone->GetZoneID(),
|
||||
zone->GetInstanceVersion(),
|
||||
zone->GetInstanceID()
|
||||
GetShortName(),
|
||||
GetZoneID(),
|
||||
GetInstanceVersion(),
|
||||
GetInstanceID()
|
||||
);
|
||||
petition_list.ClearPetitions();
|
||||
zone->SetZoneHasCurrentTime(false);
|
||||
SetZoneHasCurrentTime(false);
|
||||
if (!quiet) {
|
||||
LogInfo(
|
||||
"Zone [{}] zone_id [{}] version [{}] instance_id [{}] Going to sleep",
|
||||
zone->GetShortName(),
|
||||
zone->GetZoneID(),
|
||||
zone->GetInstanceVersion(),
|
||||
zone->GetInstanceID()
|
||||
GetShortName(),
|
||||
GetZoneID(),
|
||||
GetInstanceVersion(),
|
||||
GetInstanceID()
|
||||
);
|
||||
}
|
||||
|
||||
is_zone_loaded = false;
|
||||
|
||||
zone->ResetAuth();
|
||||
ResetAuth();
|
||||
safe_delete(zone);
|
||||
entity_list.ClearAreas();
|
||||
parse->ReloadQuests(true);
|
||||
@@ -1099,6 +1109,8 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
|
||||
}
|
||||
|
||||
Zone::~Zone() {
|
||||
LogInfo("Zone destructor called for zone [{}]", short_name);
|
||||
|
||||
spawn2_list.Clear();
|
||||
if (worldserver.Connected()) {
|
||||
worldserver.SetZoneData(0);
|
||||
@@ -1926,6 +1938,10 @@ void Zone::Repop(bool is_forced)
|
||||
|
||||
spawn_conditions.LoadSpawnConditions(short_name, instanceid);
|
||||
|
||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||
ClearZoneState(zoneid, instanceid);
|
||||
}
|
||||
|
||||
if (!content_db.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion())) {
|
||||
LogDebug("Error in Zone::Repop: database.PopulateZoneSpawnList failed");
|
||||
}
|
||||
@@ -3192,7 +3208,7 @@ std::string Zone::GetBucketRemaining(const std::string& bucket_name)
|
||||
|
||||
void Zone::DisableRespawnTimers()
|
||||
{
|
||||
LinkedListIterator<Spawn2*> e(spawn2_list);
|
||||
LinkedListIterator<Spawn2 *> e(spawn2_list);
|
||||
|
||||
e.Reset();
|
||||
|
||||
@@ -3202,4 +3218,5 @@ void Zone::DisableRespawnTimers()
|
||||
}
|
||||
}
|
||||
|
||||
#include "zone_save_state.cpp"
|
||||
#include "zone_loot.cpp"
|
||||
|
||||
+12
-1
@@ -47,6 +47,8 @@
|
||||
#include "../common/repositories/lootdrop_entries_repository.h"
|
||||
#include "../common/repositories/base_data_repository.h"
|
||||
#include "../common/repositories/skill_caps_repository.h"
|
||||
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||
#include "../common/repositories/spawn2_disabled_repository.h"
|
||||
|
||||
struct EXPModifier
|
||||
{
|
||||
@@ -104,7 +106,7 @@ class MobMovementManager;
|
||||
class Zone {
|
||||
public:
|
||||
static bool Bootup(uint32 iZoneID, uint32 iInstanceID, bool is_static = false);
|
||||
static void Shutdown(bool quiet = false);
|
||||
void Shutdown(bool quiet = false);
|
||||
|
||||
Zone(uint32 in_zoneid, uint32 in_instanceid, const char *in_short_name);
|
||||
~Zone();
|
||||
@@ -438,6 +440,7 @@ public:
|
||||
// loot
|
||||
void LoadLootTable(const uint32 loottable_id);
|
||||
void LoadLootTables(const std::vector<uint32> in_loottable_ids);
|
||||
void LoadLootDrops(const std::vector<uint32> in_lootdrop_ids);
|
||||
void ClearLootTables();
|
||||
void ReloadLootTables();
|
||||
LoottableRepository::Loottable *GetLootTable(const uint32 loottable_id);
|
||||
@@ -460,6 +463,14 @@ public:
|
||||
inline void SetZoneServerId(uint32 id) { m_zone_server_id = id; }
|
||||
inline uint32 GetZoneServerId() const { return m_zone_server_id; }
|
||||
|
||||
// zone state
|
||||
bool LoadZoneState(
|
||||
std::unordered_map<uint32, uint32> spawn_times,
|
||||
std::vector<Spawn2DisabledRepository::Spawn2Disabled> disabled_spawns
|
||||
);
|
||||
void SaveZoneState();
|
||||
static void ClearZoneState(uint32 zone_id, uint32 instance_id);
|
||||
|
||||
private:
|
||||
bool allow_mercs;
|
||||
bool can_bind;
|
||||
|
||||
@@ -300,3 +300,92 @@ std::vector<LootdropEntriesRepository::LootdropEntries> Zone::GetLootdropEntries
|
||||
return entries;
|
||||
}
|
||||
|
||||
void Zone::LoadLootDrops(const std::vector<uint32> in_lootdrop_ids)
|
||||
{
|
||||
BenchTimer timer;
|
||||
|
||||
// copy lootdrop_ids
|
||||
std::vector<uint32> lootdrop_ids = in_lootdrop_ids;
|
||||
|
||||
// check if lootdrop is already loaded
|
||||
std::vector<uint32> loaded_drops = {};
|
||||
for (const auto &e: lootdrop_ids) {
|
||||
for (const auto &f: m_lootdrops) {
|
||||
if (e == f.id) {
|
||||
LogLootDetail("Lootdrop [{}] already loaded", e);
|
||||
loaded_drops.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove loaded drops from lootdrop_ids
|
||||
for (const auto &e: loaded_drops) {
|
||||
lootdrop_ids.erase(
|
||||
std::remove(
|
||||
lootdrop_ids.begin(),
|
||||
lootdrop_ids.end(),
|
||||
e
|
||||
),
|
||||
lootdrop_ids.end()
|
||||
);
|
||||
}
|
||||
|
||||
if (lootdrop_ids.empty()) {
|
||||
LogLootDetail("No lootdrops to load");
|
||||
return;
|
||||
}
|
||||
|
||||
auto lootdrops = LootdropRepository::GetWhere(
|
||||
content_db,
|
||||
fmt::format(
|
||||
"id IN ({})",
|
||||
Strings::Join(lootdrop_ids, ",")
|
||||
)
|
||||
);
|
||||
|
||||
auto lootdrop_entries = LootdropEntriesRepository::GetWhere(
|
||||
content_db,
|
||||
fmt::format(
|
||||
"lootdrop_id IN ({})",
|
||||
Strings::Join(lootdrop_ids, ",")
|
||||
)
|
||||
);
|
||||
|
||||
// emplace back drops to m_lootdrops if not exists
|
||||
for (const auto &e: lootdrops) {
|
||||
bool has_drop = false;
|
||||
|
||||
for (const auto &l: m_lootdrops) {
|
||||
if (e.id == l.id) {
|
||||
has_drop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_entry = false;
|
||||
if (!has_drop) {
|
||||
// add lootdrop
|
||||
m_lootdrops.emplace_back(e);
|
||||
|
||||
// add lootdrop entries
|
||||
for (const auto &f: lootdrop_entries) {
|
||||
if (e.id == f.lootdrop_id) {
|
||||
|
||||
// check if lootdrop entry already exists in memory
|
||||
has_entry = false;
|
||||
for (const auto &g: m_lootdrop_entries) {
|
||||
if (f.lootdrop_id == g.lootdrop_id && f.item_id == g.item_id && f.multiplier == g.multiplier) {
|
||||
has_entry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lootdrop_ids.empty()) {
|
||||
LogInfo("Loaded [{}] lootdrops ({}s)", m_lootdrops.size(), std::to_string(timer.elapsed()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
#include <string>
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <cereal/types/map.hpp>
|
||||
#include "npc.h"
|
||||
#include "corpse.h"
|
||||
#include "zone.h"
|
||||
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||
#include "../common/repositories/spawn2_disabled_repository.h"
|
||||
|
||||
struct LootEntryStateData {
|
||||
uint32 item_id;
|
||||
uint32_t lootdrop_id;
|
||||
uint16 charges = 0; // used in dynamically added loot (AddItem)
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(lootdrop_id),
|
||||
CEREAL_NVP(charges)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct LootStateData {
|
||||
uint32 copper = 0;
|
||||
uint32 silver = 0;
|
||||
uint32 gold = 0;
|
||||
uint32 platinum = 0;
|
||||
std::vector<LootEntryStateData> entries = {};
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(copper),
|
||||
CEREAL_NVP(silver),
|
||||
CEREAL_NVP(gold),
|
||||
CEREAL_NVP(platinum),
|
||||
CEREAL_NVP(entries)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool IsValidJson(const std::string& json) {
|
||||
rapidjson::Document doc;
|
||||
rapidjson::ParseResult result = doc.Parse(json.c_str());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void LoadLootStateData(Zone *zone, NPC *npc, const std::string &loot_data)
|
||||
{
|
||||
LootStateData l{};
|
||||
|
||||
if (!IsValidJson(loot_data)) {
|
||||
LogZoneState("Invalid JSON data for NPC [{}]", npc->GetNPCTypeID());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << loot_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
l.serialize(ar);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
LogZoneState("Failed to load loot state data for NPC [{}] [{}]", npc->GetNPCTypeID(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
npc->AddLootCash(l.copper, l.silver, l.gold, l.platinum);
|
||||
|
||||
for (auto &e: l.entries) {
|
||||
const auto *db_item = database.GetItem(e.item_id);
|
||||
if (!db_item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// dynamically added via AddItem
|
||||
if (e.lootdrop_id == 0) {
|
||||
npc->AddItem(e.item_id, e.charges);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto entries = zone->GetLootdropEntries(e.lootdrop_id);
|
||||
if (entries.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LootdropEntriesRepository::LootdropEntries lootdrop_entry;
|
||||
|
||||
for (auto &le: entries) {
|
||||
if (e.item_id == le.item_id) {
|
||||
lootdrop_entry = le;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
npc->AddLootDrop(db_item, lootdrop_entry);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string GetLootSerialized(NPC *npc)
|
||||
{
|
||||
LootStateData ls = {};
|
||||
auto loot_items = npc->GetLootItems(); // Assuming this returns a list of loot items
|
||||
ls.copper = npc->GetCopper();
|
||||
ls.silver = npc->GetSilver();
|
||||
ls.gold = npc->GetGold();
|
||||
ls.platinum = npc->GetPlatinum();
|
||||
ls.entries.reserve(loot_items.size());
|
||||
|
||||
for (auto &l: loot_items) {
|
||||
ls.entries.emplace_back(
|
||||
LootEntryStateData{
|
||||
.item_id = l->item_id,
|
||||
.lootdrop_id = l->lootdrop_id,
|
||||
.charges = l->charges,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
std::stringstream ss;
|
||||
{
|
||||
cereal::JSONOutputArchiveSingleLine ar(ss);
|
||||
ls.serialize(ar);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
} catch (const std::exception &e) {
|
||||
LogZoneState("Failed to serialize loot data for NPC [{}] [{}]", npc->GetNPCTypeID(), e.what());
|
||||
return "";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
inline std::string GetLootSerialized(Corpse *c)
|
||||
{
|
||||
LootStateData ls = {};
|
||||
auto loot_items = c->GetLootItems(); // Assuming this returns a list of loot items
|
||||
ls.copper = c->GetCopper();
|
||||
ls.silver = c->GetSilver();
|
||||
ls.gold = c->GetGold();
|
||||
ls.platinum = c->GetPlatinum();
|
||||
ls.entries.reserve(loot_items.size());
|
||||
|
||||
for (auto &l: loot_items) {
|
||||
ls.entries.emplace_back(
|
||||
LootEntryStateData{
|
||||
.item_id = l->item_id,
|
||||
.lootdrop_id = l->lootdrop_id,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
std::stringstream ss;
|
||||
{
|
||||
cereal::JSONOutputArchiveSingleLine ar(ss);
|
||||
ls.serialize(ar);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
} catch (const std::exception &e) {
|
||||
LogZoneState("Failed to serialize loot data for Corpse [{}] [{}]", c->GetID(), e.what());
|
||||
return "";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
inline void LoadNPCEntityVariables(NPC *n, const std::string &entity_variables)
|
||||
{
|
||||
if (!IsValidJson(entity_variables)) {
|
||||
LogZoneState("Invalid JSON data for NPC [{}]", n->GetNPCTypeID());
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> deserialized_map;
|
||||
try {
|
||||
std::istringstream is(entity_variables);
|
||||
{
|
||||
cereal::JSONInputArchive archive(is);
|
||||
archive(deserialized_map);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
LogZoneState("Failed to load entity variables for NPC [{}] [{}]", n->GetNPCTypeID(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &[key, value]: deserialized_map) {
|
||||
n->SetEntityVariable(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
inline void LoadNPCBuffs(NPC *n, const std::string &buffs)
|
||||
{
|
||||
if (!IsValidJson(buffs)) {
|
||||
LogZoneState("Invalid JSON data for NPC [{}]", n->GetNPCTypeID());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Buffs_Struct> valid_buffs;
|
||||
try {
|
||||
std::istringstream is(buffs);
|
||||
{
|
||||
cereal::JSONInputArchive archive(is);
|
||||
archive(cereal::make_nvp("buffs", valid_buffs));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
LogZoneState("Failed to load entity variables for NPC [{}] [{}]", n->GetNPCTypeID(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &b: valid_buffs) {
|
||||
// int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1, bool disable_buff_overwrite = false);
|
||||
n->AddBuff(n, b.spellid, b.ticsremaining, b.casterlevel, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<uint32_t> GetLootdropIds(const std::vector<ZoneStateSpawnsRepository::ZoneStateSpawns> &spawn_states)
|
||||
{
|
||||
LogInfo("Loading lootdrop ids for zone state spawns");
|
||||
|
||||
std::vector<uint32_t> lootdrop_ids;
|
||||
for (auto &s: spawn_states) {
|
||||
if (s.loot_data.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LootStateData l{};
|
||||
try {
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << s.loot_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
l.serialize(ar);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
LogZoneState("Failed to load loot state data for spawn2 [{}] [{}]", s.id, e.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &e: l.entries) {
|
||||
// make sure it isn't already in the list
|
||||
if (std::find(lootdrop_ids.begin(), lootdrop_ids.end(), e.lootdrop_id) == lootdrop_ids.end()) {
|
||||
lootdrop_ids.push_back(e.lootdrop_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo("Loaded [{}] lootdrop id(s)", lootdrop_ids.size());
|
||||
|
||||
return lootdrop_ids;
|
||||
}
|
||||
|
||||
inline void LoadNPCState(Zone *zone, NPC *n, ZoneStateSpawnsRepository::ZoneStateSpawns &s)
|
||||
{
|
||||
n->SetHP(s.hp);
|
||||
n->SetMana(s.mana);
|
||||
n->SetEndurance(s.endurance);
|
||||
|
||||
if (s.grid) {
|
||||
n->AssignWaypoints(s.grid, s.current_waypoint);
|
||||
}
|
||||
|
||||
LoadLootStateData(zone, n, s.loot_data);
|
||||
LoadNPCEntityVariables(n, s.entity_variables);
|
||||
LoadNPCBuffs(n, s.buffs);
|
||||
|
||||
if (s.is_corpse) {
|
||||
auto decay_time = s.decay_in_seconds * 1000;
|
||||
if (decay_time > 0) {
|
||||
n->SetQueuedToCorpse();
|
||||
n->SetCorpseDecayTime(decay_time);
|
||||
}
|
||||
else {
|
||||
n->Depop();
|
||||
}
|
||||
}
|
||||
|
||||
n->SetResumedFromZoneSuspend(true);
|
||||
}
|
||||
|
||||
bool Zone::LoadZoneState(
|
||||
std::unordered_map<uint32, uint32> spawn_times,
|
||||
std::vector<Spawn2DisabledRepository::Spawn2Disabled> disabled_spawns
|
||||
)
|
||||
{
|
||||
auto spawn_states = ZoneStateSpawnsRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"zone_id = {} AND instance_id = {}",
|
||||
zoneid,
|
||||
zone->GetInstanceID()
|
||||
)
|
||||
);
|
||||
|
||||
LogInfo("Loading zone state spawns for zone [{}] spawns [{}]", GetShortName(), spawn_states.size());
|
||||
|
||||
std::vector<uint32_t> lootdrop_ids = GetLootdropIds(spawn_states);
|
||||
zone->LoadLootDrops(lootdrop_ids);
|
||||
|
||||
// we have to load grids first otherwise setting grid/wp will not work
|
||||
zone->initgrids_timer.Trigger();
|
||||
zone->Process();
|
||||
|
||||
for (auto &s: spawn_states) {
|
||||
if (s.spawngroup_id == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.is_corpse) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 spawn_time_left = 0;
|
||||
if (spawn_times.count(s.spawn2_id) != 0) {
|
||||
spawn_time_left = spawn_times[s.spawn2_id];
|
||||
LogInfo("Spawn2 [{}] Respawn time left [{}]", s.spawn2_id, spawn_time_left);
|
||||
}
|
||||
|
||||
// load from spawn2_disabled
|
||||
bool spawn_enabled = true;
|
||||
|
||||
// check if spawn is disabled
|
||||
for (auto &ds: disabled_spawns) {
|
||||
if (ds.spawn2_id == s.spawn2_id) {
|
||||
spawn_enabled = !ds.disabled;
|
||||
}
|
||||
}
|
||||
|
||||
auto new_spawn = new Spawn2(
|
||||
s.spawn2_id,
|
||||
s.spawngroup_id,
|
||||
s.x,
|
||||
s.y,
|
||||
s.z,
|
||||
s.heading,
|
||||
s.respawn_time,
|
||||
s.variance,
|
||||
spawn_time_left,
|
||||
s.grid,
|
||||
(bool) s.path_when_zone_idle,
|
||||
s.condition_id,
|
||||
s.condition_min_value,
|
||||
spawn_enabled,
|
||||
(EmuAppearance) s.anim
|
||||
);
|
||||
|
||||
if (spawn_time_left == 0) {
|
||||
new_spawn->SetCurrentNPCID(s.npc_id);
|
||||
}
|
||||
|
||||
spawn2_list.Insert(new_spawn);
|
||||
new_spawn->Process();
|
||||
auto n = new_spawn->GetNPC();
|
||||
if (n) {
|
||||
n->ClearLootItems();
|
||||
if (s.grid > 0) {
|
||||
n->AssignWaypoints(s.grid, s.current_waypoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dynamic spawns, quest spawns, triggers etc.
|
||||
for (auto &s: spawn_states) {
|
||||
if (s.spawngroup_id > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto npc_type = content_db.LoadNPCTypesData(s.npc_id);
|
||||
if (!npc_type) {
|
||||
LogZoneState("Failed to load NPC type data for npc_id [{}]", s.npc_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto npc = new NPC(
|
||||
npc_type,
|
||||
nullptr,
|
||||
glm::vec4(s.x, s.y, s.z, s.heading),
|
||||
GravityBehavior::Water
|
||||
);
|
||||
|
||||
entity_list.AddNPC(npc, true, true);
|
||||
|
||||
LoadNPCState(zone, npc, s);
|
||||
}
|
||||
|
||||
// any NPC that is spawned by the spawn system
|
||||
for (auto &e: entity_list.GetNPCList()) {
|
||||
auto npc = e.second;
|
||||
if (npc->GetSpawnGroupId() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &s: spawn_states) {
|
||||
bool is_same_npc =
|
||||
s.npc_id == npc->GetNPCTypeID() &&
|
||||
s.spawn2_id == npc->GetSpawnPointID() &&
|
||||
s.spawngroup_id == npc->GetSpawnGroupId();
|
||||
if (is_same_npc) {
|
||||
LoadNPCState(zone, npc, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !spawn_states.empty();
|
||||
}
|
||||
|
||||
inline void SaveNPCState(NPC *n, ZoneStateSpawnsRepository::ZoneStateSpawns &s)
|
||||
{
|
||||
// entity variables
|
||||
std::map<std::string, std::string> variables;
|
||||
for (const auto &k: n->GetEntityVariables()) {
|
||||
variables[k] = n->GetEntityVariable(k);
|
||||
}
|
||||
|
||||
try {
|
||||
std::ostringstream os;
|
||||
{
|
||||
cereal::JSONOutputArchiveSingleLine archive(os);
|
||||
archive(variables);
|
||||
}
|
||||
s.entity_variables = os.str();
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
LogZoneState("Failed to serialize entity variables for NPC [{}] [{}]", n->GetNPCTypeID(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
// buffs
|
||||
auto buffs = n->GetBuffs();
|
||||
if (!buffs) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Buffs_Struct> valid_buffs;
|
||||
|
||||
for (int index = 0; index < n->GetMaxBuffSlots(); index++) {
|
||||
if (buffs[index].spellid != 0 && buffs[index].spellid != 65535) {
|
||||
valid_buffs.push_back(buffs[index]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
std::ostringstream os = std::ostringstream();
|
||||
{
|
||||
cereal::JSONOutputArchiveSingleLine archive(os);
|
||||
archive(cereal::make_nvp("buffs", valid_buffs));
|
||||
}
|
||||
s.buffs = os.str();
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
LogZoneState("Failed to serialize buffs for NPC [{}] [{}]", n->GetNPCTypeID(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
// rest
|
||||
s.npc_id = n->GetNPCTypeID();
|
||||
s.loot_data = GetLootSerialized(n);
|
||||
s.hp = n->GetHP();
|
||||
s.mana = n->GetMana();
|
||||
s.endurance = n->GetEndurance();
|
||||
s.grid = n->GetGrid();
|
||||
s.current_waypoint = n->GetGrid() > 0 ? n->GetCWP() : 0;
|
||||
s.x = n->GetX();
|
||||
s.y = n->GetY();
|
||||
s.z = n->GetZ();
|
||||
s.heading = n->GetHeading();
|
||||
s.created_at = std::time(nullptr);
|
||||
}
|
||||
|
||||
void Zone::SaveZoneState()
|
||||
{
|
||||
// spawns
|
||||
std::vector<ZoneStateSpawnsRepository::ZoneStateSpawns> spawns = {};
|
||||
LinkedListIterator<Spawn2 *> iterator(spawn2_list);
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
Spawn2 *sp = iterator.GetData();
|
||||
auto s = ZoneStateSpawnsRepository::NewEntity();
|
||||
s.zone_id = GetZoneID();
|
||||
s.instance_id = GetInstanceID();
|
||||
s.npc_id = sp->CurrentNPCID();
|
||||
s.spawn2_id = sp->GetID();
|
||||
s.spawngroup_id = sp->SpawnGroupID();
|
||||
s.x = sp->GetX();
|
||||
s.y = sp->GetY();
|
||||
s.z = sp->GetZ();
|
||||
s.heading = sp->GetHeading();
|
||||
s.respawn_time = sp->RespawnTimer();
|
||||
s.variance = sp->GetVariance();
|
||||
s.grid = sp->GetGrid();
|
||||
s.path_when_zone_idle = sp->GetPathWhenZoneIdle() ? 1 : 0;
|
||||
s.condition_id = sp->GetSpawnCondition();
|
||||
s.condition_min_value = sp->GetConditionMinValue();
|
||||
s.enabled = sp->Enabled() ? 1 : 0;
|
||||
s.anim = sp->GetAnimation();
|
||||
s.created_at = std::time(nullptr);
|
||||
|
||||
auto n = sp->GetNPC();
|
||||
if (n) {
|
||||
SaveNPCState(n, s);
|
||||
}
|
||||
|
||||
spawns.emplace_back(s);
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
// npcs that are not in the spawn2 list
|
||||
for (auto &n: entity_list.GetNPCList()) {
|
||||
// everything below here is dynamically spawned
|
||||
bool ignore_npcs =
|
||||
n.second->GetSpawnGroupId() > 0 ||
|
||||
n.second->GetNPCTypeID() < 100 ||
|
||||
n.second->HasOwner();
|
||||
if (ignore_npcs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto s = ZoneStateSpawnsRepository::NewEntity();
|
||||
s.zone_id = GetZoneID();
|
||||
s.instance_id = GetInstanceID();
|
||||
|
||||
SaveNPCState(n.second, s);
|
||||
|
||||
spawns.emplace_back(s);
|
||||
}
|
||||
|
||||
// corpses
|
||||
for (auto &n: entity_list.GetCorpseList()) {
|
||||
if (!n.second->IsNPCCorpse()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto s = ZoneStateSpawnsRepository::NewEntity();
|
||||
s.zone_id = GetZoneID();
|
||||
s.instance_id = GetInstanceID();
|
||||
s.npc_id = n.second->GetNPCTypeID();
|
||||
s.is_corpse = 1;
|
||||
s.x = n.second->GetX();
|
||||
s.y = n.second->GetY();
|
||||
s.z = n.second->GetZ();
|
||||
s.heading = n.second->GetHeading();
|
||||
s.created_at = std::time(nullptr);
|
||||
s.loot_data = GetLootSerialized(n.second);
|
||||
s.decay_in_seconds = (int) (n.second->GetDecayTime() / 1000);
|
||||
|
||||
spawns.emplace_back(s);
|
||||
}
|
||||
|
||||
ZoneStateSpawnsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`zone_id` = {} AND `instance_id` = {}",
|
||||
GetZoneID(),
|
||||
GetInstanceID()
|
||||
)
|
||||
);
|
||||
|
||||
ZoneStateSpawnsRepository::InsertMany(database, spawns);
|
||||
|
||||
LogInfo("Saved [{}] zone state spawns", Strings::Commify(spawns.size()));
|
||||
}
|
||||
|
||||
void Zone::ClearZoneState(uint32 zone_id, uint32 instance_id)
|
||||
{
|
||||
ZoneStateSpawnsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`zone_id` = {} AND `instance_id` = {}",
|
||||
zone_id,
|
||||
instance_id
|
||||
)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user