Compare commits

..

36 Commits

Author SHA1 Message Date
Akkadius d41725e325 [Hotfix] Fix sigabort crash from invalid JSON 2025-03-03 01:47:34 -06:00
Akkadius 88580b69b6 [Hotfix] Remove one port check in world 2025-03-03 00:49:02 -06:00
Akkadius f7a6fe595a [Release] 23.2.0 2025-03-03 00:17:54 -06:00
Chris Miles 07d14c2681 [Crash] Fix crash in add loot code path (#4745) 2025-03-03 00:16:51 -06:00
Chris Miles eac7a73fb6 [Crash] Potential crash fix in scan close mobs (#4744) 2025-03-03 00:16:42 -06:00
Chris Miles c5715f1f14 [Crash] Fix Aura process crash with bots (#4743) 2025-03-03 00:13:00 -06:00
Chris Miles 3902230fa1 [Crash] Fix world repop crash (#4742) 2025-03-02 22:04:39 -08:00
Chris Miles 212969f5cd [Crash] Database SetMutex crash fix (#4741) 2025-03-02 22:04:24 -08:00
Chris Miles de4226fdc9 [World] Check if port in use to avoid double booting mistakes (#4740)
* Stuff

* Potentially fix aura crash

* Reload crash fix

* Revert "Reload crash fix"

This reverts commit 96e1e76306.

* Fix

* Update entity.cpp

* Update dbcore.cpp

* [World] Check if port in use to avoid double booting mistakes

* Revert "Stuff"

This reverts commit 2162c00edd.

* Revert "Potentially fix aura crash"

This reverts commit 7c242723f4.

* Revert "Fix"

This reverts commit 8419e284d4.

* Revert "Update entity.cpp"

This reverts commit 8a1f4545a4.

* Revert "Update dbcore.cpp"

This reverts commit f0278d9591.
2025-03-02 22:04:03 -08:00
zimp-wow 8b13434197 [Bug Fix] Cleanup zone buckets on instance purge. (#4739) 2025-03-02 17:01:25 -06:00
catapultam-habeo 27274397ec [Bug Fix] Fix an error causing Endurance Regen to not be applied by items. (#4738)
* fix typo causing endurance regen to not be applied by items

* further correction
2025-03-02 03:39:22 -05:00
Chris Miles 2b79a36014 [Release] 23.1.0 (#4736)
* [Release] 23.1.0

* Version

* Pet table last minute add in
2025-03-01 19:23:50 -06:00
Akkadius 8cf52294e9 [Hotfix] Add character_pet_name to player tables schema 2025-03-01 18:56:38 -06:00
Chris Miles 0ef79903f8 [Crash] Validate item in SE_SummonItemIntoBag (#4734) 2025-03-01 18:32:43 -06:00
Chris Miles ab14458f9e [Crash] Fix large file size crash in File::GetContents for windows (#4735) 2025-03-01 18:32:35 -06:00
Chris Miles 5b94e736b3 [Crash] Fix reload concurrency crash when ran from Spire (#4733) 2025-03-01 18:05:28 -06:00
Chris Miles c3b8cc9744 [Crash] Check for directory existence before traversing in CheckForCompatibleQuestPlugins (#4730) 2025-03-01 20:01:37 -04:00
Mitch Freeman 89e3b2c72e [Fix] Add client packets to questmanager:setguild (#4732) 2025-03-01 17:55:44 -06:00
Chris Miles 23c4aa241b [Crash] World CLI validation (#4728)
* [Crash] World CLI validation

* Clean
2025-03-01 19:52:51 -04:00
Chris Miles acb7584e26 [Loginserver] Minor cleanup (#4729) 2025-03-01 19:52:21 -04:00
Chris Miles a885bd9322 [Crash] Fix filesystem crash / exception in DatabaseDumpService::RemoveCredentialsFile() (#4731) 2025-03-01 19:51:41 -04:00
Chris Miles 20fe1926e0 [Database] Remove force_interactive from big bag updates (#4727) 2025-03-01 16:55:57 -06:00
Chris Miles eb7118754b [Zone State] Wrap all serialization/deserialization in try/catch (#4726) 2025-03-01 16:46:08 -06:00
Mitch Freeman 3611b49f68 [Feature] Evolving items Additions (#4725)
* Implement multi-value for evolving sub_types

- Added ability for evolving sub_types to contain multiple values
- Implemented EvolvingItems::Types::NUMBER_OF_KILLS with level for sub_type

* Repair a timer issue preventing proper evolution of items

* Simplify

* Remove extra level of nesting

* Update client_evolving_items.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-03-01 16:38:59 -06:00
catapultam-habeo 511d8a8bb3 [Bug Fix] Refactor ApplyItemBonuses to fix double-counting of ATK and recommended levels not correctly applying (#4713)
* refactor AddItemBonuses

* typo

* simplify some syntax

* fix indents

* Revert "fix indents"

This reverts commit 8e660707a9.

* fix indents without blowing up entire file

* Revert "fix indents without blowing up entire file"

This reverts commit 6b21d1bcc9.

* ok for real though

* Undo spaces in formatting, source uses tabs

* Spaces to tabs take 2

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-02-28 21:04:42 -06:00
catapultam-habeo 1598d2e17b [Feature] Add a rule for spells to bypass stacking rules (#4716)
* define rule that allows for spells which always stack

* Update spells.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-02-28 16:49:20 -06:00
nytmyr 805757ba87 [Bots] Fix unresponsive bots in groups upon group wipe (#4712)
- Bots were failing to recall their group after dying due to the way it was grabbing the group from entity list.
2025-02-28 16:10:46 -06:00
Chris Miles eb6ac25540 [Code Cleanup] More login <-> world code cleanup (#4724)
* More cleanup

* More cleanup
2025-02-28 16:08:06 -06:00
MortimerGreenwald cb634cf57d [Bug Fix] Fix Trading Items to Bot Pets (#4721)
Small change using IsPetOwnerOfClientBot() instead of just IsPetOwnerClient

Can bot pet take item?

not before, but hopefully now?
2025-02-28 15:54:38 -06:00
Chris Miles 2f7ca2cdc8 [Zone] Implement Zone State Saving on Shutdown (#4715)
* Save spawns

* Update base_zone_state_spawns_repository.h

* Zone state save work

* Code cleanup

* More cleanup

* Database migration

* Update database_update_manifest.cpp

* Revert decay at storage model

* Code cleanup

* More cleanup

* More cleanup

* More cleanup

* Entity variables

* Add entity variables to the schema

* Post rebase

* Checkpoint

* Serialize / deserialize buffs

* Current hp / mana / end save / load

* Save / load current_waypoint

* Add zone spawn protection

* Finishing touches

* Cleanup

* Update zone_save_state.cpp

* Cleanup

* Update zone_save_state.cpp

* Update npc.cpp

* Update npc.cpp

* More

* Update perl_npc.cpp

* Update zone_loot.cpp
2025-02-28 15:31:06 -06:00
Chris Miles 425d24c1f4 [Quest API] Implement eq.handin() and quest::handin() (#4718)
* [Quest API] Implement eq.handin() and quest::handin()

* Fix MQ using new API style
2025-02-28 15:22:39 -06:00
Alex King 875df8e64a [Quest API] Add Key Ring Methods to Perl and Lua (#4719) 2025-02-28 15:08:57 -06:00
Alex King 7a2d2a0c51 [Bug Fix] Fix AA Reset Error Message (#4720)
* [Bug Fix] Fix AA Reset Error Message

* Update client.cpp

* Update client.cpp
2025-02-28 15:06:49 -06:00
Alex King 5296202e56 [Bug Fix] Fix Issue with Suffixes/Prefixes (#4723) 2025-02-28 15:05:57 -06:00
Akkadius e2db8ffea8 [Hotfix] Clear m_completed_shared_tasks before reloading 2025-02-24 17:24:18 -06:00
Chris Miles fa2ab11676 [Tasks] Extend IsTaskCompleted to also be aware of shared task completion (#4714)
* [Tasks] Extend IsTaskCompleted to also be aware of shared task completion

* Fix my stupidity

* Update client.h
2025-02-24 16:31:35 -06:00
78 changed files with 2992 additions and 1098 deletions
+78
View File
@@ -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
View File
@@ -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);
+6 -1
View File
@@ -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());
}
}
}
+69 -2
View File
@@ -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,
+1
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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
+3 -1
View File
@@ -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"
};
}
+10
View File
@@ -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
View File
@@ -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 = {}
};
}
+73
View File
@@ -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;
}
+1
View File
@@ -37,6 +37,7 @@ public:
int port
);
static bool IsIPAddress(const std::string &ip_address);
static bool IsPortInUse(const std::string& ip, int port);
};
+1
View File
@@ -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;
+3 -3
View File
@@ -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;
+1 -1
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9 -11
View File
@@ -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 [{}]",
+1 -1
View File
@@ -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);
};
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+6
View File
@@ -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);
+7
View File
@@ -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;
}
-6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+75 -24
View File
@@ -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: {
}
}
+42
View File
@@ -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 {
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+6 -3
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
*/
+7
View File
@@ -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)
+1
View File
@@ -198,6 +198,7 @@ public:
);
void ReturnHandinItems(Lua_Client c);
Lua_Spawn GetSpawn(lua_State* L);
bool IsResumedFromZoneSuspend();
};
#endif
+29 -21
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+5
View File
@@ -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]);
+11
View File
@@ -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.
*/
+11 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+89
View File
@@ -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()));
}
}
+587
View File
@@ -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
)
);
}