mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 09:06:46 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8203c034bf | |||
| 33ae51f56f | |||
| 30c39194a3 | |||
| 051ce3736f | |||
| 84708edccf | |||
| da4e9ab95b | |||
| a8fea95eab | |||
| 53610c2f0f | |||
| 9f10c12874 | |||
| a0634adb3c | |||
| a2ed6be1f5 | |||
| c33ac40567 | |||
| 9ee095b354 | |||
| 7ab32af4dc | |||
| 0c301419c2 | |||
| d6a21be25e | |||
| 1d4ba082ad | |||
| 94553501ba | |||
| da824d5178 | |||
| 5a1df38900 | |||
| 8cd7148b29 | |||
| 09e079a45e | |||
| 4bc881da4b | |||
| 0615864d51 | |||
| 3638d157b2 | |||
| d41725e325 | |||
| 88580b69b6 | |||
| f7a6fe595a | |||
| 07d14c2681 | |||
| eac7a73fb6 | |||
| c5715f1f14 | |||
| 3902230fa1 | |||
| 212969f5cd | |||
| de4226fdc9 | |||
| 8b13434197 | |||
| 27274397ec | |||
| 2b79a36014 | |||
| 8cf52294e9 | |||
| 0ef79903f8 | |||
| ab14458f9e | |||
| 5b94e736b3 | |||
| c3b8cc9744 | |||
| 89e3b2c72e | |||
| 23c4aa241b | |||
| acb7584e26 | |||
| a885bd9322 | |||
| 20fe1926e0 | |||
| eb7118754b | |||
| 3611b49f68 | |||
| 511d8a8bb3 | |||
| 1598d2e17b | |||
| 805757ba87 | |||
| eb6ac25540 | |||
| cb634cf57d | |||
| 2f7ca2cdc8 | |||
| 425d24c1f4 | |||
| 875df8e64a | |||
| 7a2d2a0c51 | |||
| 5296202e56 | |||
| e2db8ffea8 | |||
| fa2ab11676 |
+144
@@ -1,3 +1,147 @@
|
|||||||
|
## [23.3.2] 3/11/2025
|
||||||
|
|
||||||
|
### DynamicZones
|
||||||
|
|
||||||
|
* Bulk request dz member statuses on zone boot ([#4769](https://github.com/EQEmu/Server/pull/4769)) @hgtw 2025-03-11
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Improvements (Continued) ([#4768](https://github.com/EQEmu/Server/pull/4768)) @Akkadius 2025-03-11
|
||||||
|
|
||||||
|
## [23.3.0] 3/8/2025
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Fix buffs not overwriting lesser buffs ([#4756](https://github.com/EQEmu/Server/pull/4756)) @nytmyr 2025-03-06
|
||||||
|
* Fix taunting bots positioning ([#4754](https://github.com/EQEmu/Server/pull/4754)) @nytmyr 2025-03-06
|
||||||
|
* Move commanded spell map to zone ([#4755](https://github.com/EQEmu/Server/pull/4755)) @nytmyr 2025-03-06
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Fix typo in GM tradeskill combine message ([#4762](https://github.com/EQEmu/Server/pull/4762)) @nytmyr 2025-03-08
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Bot aura crash fix ([#4752](https://github.com/EQEmu/Server/pull/4752)) @nytmyr 2025-03-06
|
||||||
|
|
||||||
|
### Databuckets
|
||||||
|
|
||||||
|
* Nested Databuckets Protections and Improvements ([#4748](https://github.com/EQEmu/Server/pull/4748)) @Akkadius 2025-03-04
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add Rule for dealing with augments when an item evolves ([#4758](https://github.com/EQEmu/Server/pull/4758)) @neckkola 2025-03-08
|
||||||
|
* Allow assigning Helm Texture independently of Body Texture for Horses ([#4759](https://github.com/EQEmu/Server/pull/4759)) @catapultam-habeo 2025-03-08
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add crash checks for certain PlayerEventLogs ([#4761](https://github.com/EQEmu/Server/pull/4761)) @neckkola 2025-03-07
|
||||||
|
* Correct incorrectly calculated stat caps with Heroic Stats ([#4760](https://github.com/EQEmu/Server/pull/4760)) @catapultam-habeo 2025-03-08
|
||||||
|
* Fix sigabort crash from invalid JSON @Akkadius 2025-03-03
|
||||||
|
* Forgot to push up some changes for test output @Akkadius 2025-03-04
|
||||||
|
* Parcel Delivery Updates for two edge cases ([#4753](https://github.com/EQEmu/Server/pull/4753)) @neckkola 2025-03-06
|
||||||
|
* Remove one port check in world @Akkadius 2025-03-03
|
||||||
|
* Zero out currentnpcid whenever spawn is reset. ([#4763](https://github.com/EQEmu/Server/pull/4763)) @zimp-wow 2025-03-08
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
* Convert JSON Error to Data Buckets Logging Category ([#4747](https://github.com/EQEmu/Server/pull/4747)) @Kinglykrab 2025-03-04
|
||||||
|
|
||||||
|
### Pets
|
||||||
|
|
||||||
|
* Fix renamed pets loading as blank names ([#4751](https://github.com/EQEmu/Server/pull/4751)) @nytmyr 2025-03-05
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
* Fix EvolvingItems:PercentOfRaidExperience Description ([#4757](https://github.com/EQEmu/Server/pull/4757)) @Kinglykrab 2025-03-07
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
* Cleanup Hand-in Tests ([#4749](https://github.com/EQEmu/Server/pull/4749)) @Akkadius 2025-03-04
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Make zone controller less likely to be visible, immune to all forms of combat ([#4750](https://github.com/EQEmu/Server/pull/4750)) @Akkadius 2025-03-06
|
||||||
|
* State Save Improvements ([#4765](https://github.com/EQEmu/Server/pull/4765)) @Akkadius 2025-03-08
|
||||||
|
|
||||||
|
## [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
|
## [23.0.2] 2/21/2025
|
||||||
|
|
||||||
### Bots
|
### Bots
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ uint32 Database::CreateAccount(
|
|||||||
e.password = password;
|
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);
|
e = AccountRepository::InsertOne(*this, e);
|
||||||
|
|
||||||
|
|||||||
@@ -596,8 +596,13 @@ void DatabaseDumpService::BuildCredentialsFile()
|
|||||||
void DatabaseDumpService::RemoveCredentialsFile()
|
void DatabaseDumpService::RemoveCredentialsFile()
|
||||||
{
|
{
|
||||||
if (File::Exists(CREDENTIALS_FILE)) {
|
if (File::Exists(CREDENTIALS_FILE)) {
|
||||||
|
try {
|
||||||
std::filesystem::remove(CREDENTIALS_FILE);
|
std::filesystem::remove(CREDENTIALS_FILE);
|
||||||
}
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
LogError("std::filesystem::remove err [{}]", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDumpService::IsDumpStaticInstanceData()
|
bool DatabaseDumpService::IsDumpStaticInstanceData()
|
||||||
|
|||||||
@@ -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`);
|
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false,
|
.content_schema_update = false,
|
||||||
.force_interactive = true
|
.force_interactive = false
|
||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9298,
|
.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
|
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false,
|
.content_schema_update = false,
|
||||||
.force_interactive = true
|
.force_interactive = false
|
||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9299,
|
.version = 9299,
|
||||||
@@ -6845,7 +6845,7 @@ RENAME TABLE `expedition_lockouts` TO `dynamic_zone_lockouts`;
|
|||||||
.condition = "empty",
|
.condition = "empty",
|
||||||
.match = "",
|
.match = "",
|
||||||
.sql = R"(
|
.sql = R"(
|
||||||
-- ✅ Drop old indexes
|
-- Drop old indexes if exists
|
||||||
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
||||||
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
||||||
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||||
@@ -6863,11 +6863,130 @@ ALTER TABLE `data_buckets`
|
|||||||
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
||||||
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
|
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
|
||||||
|
|
||||||
-- ✅ Create optimized unique index with `key` first
|
-- Create optimized unique index with `key` first
|
||||||
CREATE UNIQUE INDEX `keys` ON data_buckets (`key`, character_id, npc_id, bot_id, account_id, zone_id, instance_id);
|
CREATE UNIQUE INDEX `keys` ON data_buckets (`key`, character_id, npc_id, bot_id, account_id, zone_id, instance_id);
|
||||||
|
|
||||||
-- ✅ Create indexes for just instance_id (instance deletion)
|
-- Create indexes for just instance_id (instance deletion)
|
||||||
CREATE INDEX idx_instance_id ON data_buckets (instance_id);
|
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;
|
||||||
|
)",
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9310,
|
||||||
|
.description = "2025_03_7_expand_horse_def.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `horses` LIKE 'helmtexture'",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "TINYINT(2)",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `horses`
|
||||||
|
ADD COLUMN `helmtexture` TINYINT(2) NOT NULL DEFAULT -1 AFTER `texture`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = true
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9311,
|
||||||
|
.description = "2025_03_09_add_zone_state_is_zone_field.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `zone_state_spawns` LIKE 'is_zone'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `zone_state_spawns`
|
||||||
|
ADD COLUMN `is_zone` tinyint(11) NULL DEFAULT 0 AFTER `is_corpse`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9312,
|
||||||
|
.description = "2025_03_11_data_bucket_indexes.sql",
|
||||||
|
.check = "SHOW INDEX FROM data_buckets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_zone_instance_expires",
|
||||||
|
.sql = R"(
|
||||||
|
DROP INDEX IF EXISTS `idx_zone_instance_expires` ON `data_buckets`;
|
||||||
|
DROP INDEX IF EXISTS `idx_character_expires` ON `data_buckets`;
|
||||||
|
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_zone_instance_expires (zone_id, instance_id, expires);
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_character_expires (character_id, expires);
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_bot_expires (bot_id, expires);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9313,
|
||||||
|
.description = "2025_03_11_data_bucket_indexes.sql",
|
||||||
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_zone_instance",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE zone_state_spawns ADD INDEX idx_zone_instance (zone_id, instance_id);
|
||||||
|
ALTER TABLE zone_state_spawns ADD INDEX idx_instance_id (instance_id);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false
|
.content_schema_update = false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/repositories/spawn_condition_values_repository.h"
|
#include "../common/repositories/spawn_condition_values_repository.h"
|
||||||
#include "repositories/spawn2_disabled_repository.h"
|
#include "repositories/spawn2_disabled_repository.h"
|
||||||
#include "repositories/data_buckets_repository.h"
|
#include "repositories/data_buckets_repository.h"
|
||||||
|
#include "repositories/zone_state_spawns_repository.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@@ -480,6 +480,9 @@ void Database::DeleteInstance(uint16 instance_id)
|
|||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||||
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
|
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
|
||||||
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` = {}", instance_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
|
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
|
||||||
@@ -562,6 +565,10 @@ void Database::PurgeExpiredInstances()
|
|||||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", 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));
|
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));
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", imploded_instance_ids));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ namespace DatabaseSchema {
|
|||||||
"character_pet_buffs",
|
"character_pet_buffs",
|
||||||
"character_pet_info",
|
"character_pet_info",
|
||||||
"character_pet_inventory",
|
"character_pet_inventory",
|
||||||
|
"character_pet_name",
|
||||||
"character_peqzone_flags",
|
"character_peqzone_flags",
|
||||||
"character_potionbelt",
|
"character_potionbelt",
|
||||||
"character_skills",
|
"character_skills",
|
||||||
@@ -350,6 +351,7 @@ namespace DatabaseSchema {
|
|||||||
"shared_task_dynamic_zones",
|
"shared_task_dynamic_zones",
|
||||||
"shared_task_members",
|
"shared_task_members",
|
||||||
"shared_tasks",
|
"shared_tasks",
|
||||||
|
"zone_state_spawns",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,7 +302,9 @@ std::string DBcore::Escape(const std::string& s)
|
|||||||
|
|
||||||
void DBcore::SetMutex(Mutex *mutex)
|
void DBcore::SetMutex(Mutex *mutex)
|
||||||
{
|
{
|
||||||
|
if (m_mutex && m_mutex != mutex) {
|
||||||
safe_delete(m_mutex);
|
safe_delete(m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
DBcore::m_mutex = mutex;
|
DBcore::m_mutex = mutex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
|
|||||||
*/
|
*/
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|
||||||
struct LoginInfo_Struct {
|
struct LoginInfo {
|
||||||
/*000*/ char login_info[64];
|
/*000*/ char login_info[64];
|
||||||
/*064*/ uint8 unknown064[124];
|
/*064*/ uint8 unknown064[124];
|
||||||
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ void EQEmuLogSys::EnableConsoleLogging()
|
|||||||
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
|
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
|
||||||
{
|
{
|
||||||
InjectTablesIfNotExist();
|
InjectTablesIfNotExist();
|
||||||
|
|
||||||
@@ -699,6 +699,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (silent_load) {
|
||||||
|
SilenceConsoleLogging();
|
||||||
|
}
|
||||||
|
|
||||||
LogInfo("Loaded [{}] log categories", categories.size());
|
LogInfo("Loaded [{}] log categories", categories.size());
|
||||||
|
|
||||||
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
||||||
@@ -716,6 +720,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
|||||||
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
||||||
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
||||||
|
|
||||||
|
if (silent_load) {
|
||||||
|
SilenceConsoleLogging();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ namespace Logs {
|
|||||||
BotSpellChecks,
|
BotSpellChecks,
|
||||||
BotSpellTypeChecks,
|
BotSpellTypeChecks,
|
||||||
NpcHandin,
|
NpcHandin,
|
||||||
|
ZoneState,
|
||||||
MaxCategoryID /* Don't Remove this */
|
MaxCategoryID /* Don't Remove this */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,7 +257,8 @@ namespace Logs {
|
|||||||
"Bot Settings",
|
"Bot Settings",
|
||||||
"Bot Spell Checks",
|
"Bot Spell Checks",
|
||||||
"Bot Spell Type Checks",
|
"Bot Spell Type Checks",
|
||||||
"NpcHandin"
|
"NpcHandin",
|
||||||
|
"ZoneState"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +279,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void CloseFileLogs();
|
void CloseFileLogs();
|
||||||
EQEmuLogSys *LoadLogSettingsDefaults();
|
EQEmuLogSys *LoadLogSettingsDefaults();
|
||||||
EQEmuLogSys *LoadLogDatabaseSettings();
|
EQEmuLogSys *LoadLogDatabaseSettings(bool silent_load = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param directory_name
|
* @param directory_name
|
||||||
|
|||||||
@@ -914,6 +914,16 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} 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 {\
|
#define Log(debug_level, log_category, message, ...) do {\
|
||||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
|||||||
+12
-13
@@ -39,6 +39,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -90,23 +91,21 @@ std::string File::GetCwd()
|
|||||||
|
|
||||||
FileContentsResult File::GetContents(const std::string &file_name)
|
FileContentsResult File::GetContents(const std::string &file_name)
|
||||||
{
|
{
|
||||||
std::string error;
|
std::ifstream f(file_name, std::ios::in | std::ios::binary);
|
||||||
std::ifstream f;
|
if (!f) {
|
||||||
f.open(file_name);
|
return { .error = fmt::format("Couldn't open file [{}]", file_name) };
|
||||||
std::string line;
|
}
|
||||||
|
|
||||||
|
constexpr size_t CHUNK_SIZE = 4096; // Read 4KB chunks
|
||||||
std::string lines;
|
std::string lines;
|
||||||
if (f.is_open()) {
|
std::vector<char> buffer(CHUNK_SIZE);
|
||||||
while (f) {
|
|
||||||
std::getline(f, line);
|
while (f.read(buffer.data(), CHUNK_SIZE) || f.gcount() > 0) {
|
||||||
lines += line + "\n";
|
lines.append(buffer.data(), f.gcount());
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
error = fmt::format("Couldn't open file [{}]", file_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileContentsResult{
|
return FileContentsResult{
|
||||||
.contents = lines,
|
.contents = lines,
|
||||||
.error = error,
|
.error = {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,3 +259,76 @@ bool IpUtil::IsIPAddress(const std::string &ip_address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h> // For inet_pton
|
||||||
|
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h> // For inet_pton
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||||
|
bool in_use = false;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsaData;
|
||||||
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
std::cerr << "WSAStartup failed\n";
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in addr{};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
// Convert IP address from string to binary format
|
||||||
|
if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0) {
|
||||||
|
std::cerr << "Invalid IP address format: " << ip << std::endl;
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(sock);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
|
in_use = true; // Bind failed, port is in use
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(sock);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return in_use;
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
int port
|
int port
|
||||||
);
|
);
|
||||||
static bool IsIPAddress(const std::string &ip_address);
|
static bool IsIPAddress(const std::string &ip_address);
|
||||||
|
static bool IsPortInUse(const std::string& ip, int port);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct LootItem {
|
|||||||
uint16 trivial_max_level;
|
uint16 trivial_max_level;
|
||||||
uint16 npc_min_level;
|
uint16 npc_min_level;
|
||||||
uint16 npc_max_level;
|
uint16 npc_max_level;
|
||||||
|
uint32 lootdrop_id; // required for zone state referencing
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::list<LootItem*> LootItems;
|
typedef std::list<LootItem*> LootItems;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (opcode == ServerOP_UsertoWorldReq) {
|
if (opcode == ServerOP_UsertoWorldReq) {
|
||||||
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
|
auto req_in = (UsertoWorldRequest*)p.Data();
|
||||||
|
|
||||||
EQ::Net::DynamicPacket req;
|
EQ::Net::DynamicPacket req;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -45,7 +45,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opcode == ServerOP_LSClientAuth) {
|
if (opcode == ServerOP_LSClientAuth) {
|
||||||
auto req_in = (ClientAuth_Struct*)p.Data();
|
auto req_in = (ClientAuth*)p.Data();
|
||||||
|
|
||||||
EQ::Net::DynamicPacket req;
|
EQ::Net::DynamicPacket req;
|
||||||
size_t i = 0;
|
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.PutData(i, req_in->key, 30); i += 30;
|
||||||
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
||||||
req.PutUInt16(i, req_in->is_world_admin); i += 2;
|
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;
|
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
||||||
|
|
||||||
EQ::Net::DynamicPacket out;
|
EQ::Net::DynamicPacket out;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void PathManager::LoadPaths()
|
|||||||
constexpr int path_width = 0;
|
constexpr int path_width = 0;
|
||||||
constexpr int break_length = 70;
|
constexpr int break_length = 70;
|
||||||
|
|
||||||
std::cout << std::endl;
|
LogInfo("Loading server paths");
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
LogInfo("{}", Strings::Repeat("-", break_length));
|
||||||
for (const auto& [name, in_path] : paths) {
|
for (const auto& [name, in_path] : paths) {
|
||||||
if (!in_path.empty()) {
|
if (!in_path.empty()) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public:
|
|||||||
int16_t race;
|
int16_t race;
|
||||||
int8_t gender;
|
int8_t gender;
|
||||||
int8_t texture;
|
int8_t texture;
|
||||||
|
int8_t helmtexture;
|
||||||
float mountspeed;
|
float mountspeed;
|
||||||
std::string notes;
|
std::string notes;
|
||||||
};
|
};
|
||||||
@@ -41,6 +42,7 @@ public:
|
|||||||
"race",
|
"race",
|
||||||
"gender",
|
"gender",
|
||||||
"texture",
|
"texture",
|
||||||
|
"helmtexture",
|
||||||
"mountspeed",
|
"mountspeed",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -54,6 +56,7 @@ public:
|
|||||||
"race",
|
"race",
|
||||||
"gender",
|
"gender",
|
||||||
"texture",
|
"texture",
|
||||||
|
"helmtexture",
|
||||||
"mountspeed",
|
"mountspeed",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -101,6 +104,7 @@ public:
|
|||||||
e.race = 216;
|
e.race = 216;
|
||||||
e.gender = 0;
|
e.gender = 0;
|
||||||
e.texture = 0;
|
e.texture = 0;
|
||||||
|
e.helmtexture = -1;
|
||||||
e.mountspeed = 0.75;
|
e.mountspeed = 0.75;
|
||||||
e.notes = "Notes";
|
e.notes = "Notes";
|
||||||
|
|
||||||
@@ -144,8 +148,9 @@ public:
|
|||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -183,8 +188,9 @@ public:
|
|||||||
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.mountspeed));
|
v.push_back(columns[5] + " = " + std::to_string(e.helmtexture));
|
||||||
v.push_back(columns[6] + " = '" + Strings::Escape(e.notes) + "'");
|
v.push_back(columns[6] + " = " + std::to_string(e.mountspeed));
|
||||||
|
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -211,6 +217,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -247,6 +254,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -287,8 +295,9 @@ public:
|
|||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -318,8 +327,9 @@ public:
|
|||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -399,6 +409,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -428,6 +439,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public:
|
|||||||
uint32_t item_evolve_level;
|
uint32_t item_evolve_level;
|
||||||
uint32_t item_id;
|
uint32_t item_id;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t sub_type;
|
std::string sub_type;
|
||||||
int64_t required_amount;
|
int64_t required_amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ public:
|
|||||||
e.item_evolve_level = 0;
|
e.item_evolve_level = 0;
|
||||||
e.item_id = 0;
|
e.item_id = 0;
|
||||||
e.type = 0;
|
e.type = 0;
|
||||||
e.sub_type = 0;
|
e.sub_type = "0";
|
||||||
e.required_amount = 0;
|
e.required_amount = 0;
|
||||||
|
|
||||||
return e;
|
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_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.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.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;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -183,7 +183,7 @@ public:
|
|||||||
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
|
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[3] + " = " + std::to_string(e.item_id));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.type));
|
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));
|
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
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_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
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));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
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_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
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));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
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_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.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.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;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
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_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.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.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;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
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_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
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));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
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_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
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));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
@@ -0,0 +1,715 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
int8_t is_zone;
|
||||||
|
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",
|
||||||
|
"is_zone",
|
||||||
|
"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",
|
||||||
|
"is_zone",
|
||||||
|
"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.is_zone = 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.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-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.is_zone));
|
||||||
|
v.push_back(columns[5] + " = " + std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(columns[6] + " = " + std::to_string(e.npc_id));
|
||||||
|
v.push_back(columns[7] + " = " + std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(columns[8] + " = " + std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(columns[9] + " = " + std::to_string(e.x));
|
||||||
|
v.push_back(columns[10] + " = " + std::to_string(e.y));
|
||||||
|
v.push_back(columns[11] + " = " + std::to_string(e.z));
|
||||||
|
v.push_back(columns[12] + " = " + std::to_string(e.heading));
|
||||||
|
v.push_back(columns[13] + " = " + std::to_string(e.respawn_time));
|
||||||
|
v.push_back(columns[14] + " = " + std::to_string(e.variance));
|
||||||
|
v.push_back(columns[15] + " = " + std::to_string(e.grid));
|
||||||
|
v.push_back(columns[16] + " = " + std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(columns[17] + " = " + std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(columns[18] + " = " + std::to_string(e.condition_id));
|
||||||
|
v.push_back(columns[19] + " = " + std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(columns[20] + " = " + std::to_string(e.enabled));
|
||||||
|
v.push_back(columns[21] + " = " + std::to_string(e.anim));
|
||||||
|
v.push_back(columns[22] + " = '" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back(columns[23] + " = '" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back(columns[24] + " = '" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(columns[25] + " = " + std::to_string(e.hp));
|
||||||
|
v.push_back(columns[26] + " = " + std::to_string(e.mana));
|
||||||
|
v.push_back(columns[27] + " = " + std::to_string(e.endurance));
|
||||||
|
v.push_back(columns[28] + " = 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.is_zone));
|
||||||
|
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.is_zone));
|
||||||
|
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.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-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.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-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.is_zone));
|
||||||
|
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.is_zone));
|
||||||
|
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
|
||||||
+14
-10
@@ -374,6 +374,9 @@ 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, 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, 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, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
||||||
|
RULE_BOOL(Zone, StateSaveEntityVariables, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
RULE_BOOL(Zone, StateSaveBuffs, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
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_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Map)
|
RULE_CATEGORY(Map)
|
||||||
@@ -528,6 +531,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, 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_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_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_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Combat)
|
RULE_CATEGORY(Combat)
|
||||||
@@ -848,17 +852,15 @@ RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery A
|
|||||||
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
||||||
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
||||||
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
||||||
RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.")
|
RULE_REAL(Bots, LowerMeleeDistanceMultiplier, 0.35, "Closest % of the hit box a melee bot will get to the target. Default 0.35")
|
||||||
RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.")
|
RULE_REAL(Bots, LowerTauntingMeleeDistanceMultiplier, 0.25, "Closest % of the hit box a taunting melee bot will get to the target. Default 0.25")
|
||||||
RULE_REAL(Bots, PercentMinMeleeDistance, 0.75, "Multiplier of the their melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.")
|
RULE_REAL(Bots, LowerMaxMeleeRangeDistanceMultiplier, 0.80, "Closest % of the hit box a max melee range melee bot will get to the target. Default 0.80")
|
||||||
RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.")
|
RULE_REAL(Bots, UpperMeleeDistanceMultiplier, 0.55, "Furthest % of the hit box a melee bot will get from the target. Default 0.55")
|
||||||
RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .")
|
RULE_REAL(Bots, UpperTauntingMeleeDistanceMultiplier, 0.45, "Furthest % of the hit box a taunting melee bot will get from the target. Default 0.45")
|
||||||
RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.40, "Multiplier of their melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.")
|
RULE_REAL(Bots, UpperMaxMeleeRangeDistanceMultiplier, 0.95, "Furthest % of the hit box a max melee range melee bot will get from the target. Default 0.95")
|
||||||
RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.")
|
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
|
||||||
RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.")
|
|
||||||
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
|
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
|
||||||
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
|
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
|
||||||
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "True Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.")
|
|
||||||
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
||||||
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
||||||
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
||||||
@@ -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, 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, 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, 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_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Parcel)
|
RULE_CATEGORY(Parcel)
|
||||||
@@ -1160,8 +1163,9 @@ RULE_CATEGORY_END()
|
|||||||
RULE_CATEGORY(EvolvingItems)
|
RULE_CATEGORY(EvolvingItems)
|
||||||
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
||||||
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
|
||||||
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of raid experience allocated to evolving items that require experience.")
|
||||||
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
|
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
|
||||||
|
RULE_BOOL(EvolvingItems, DestroyAugmentsOnEvolve, false, "If this is enabled, any augments in an item will be destroyed when the item evolves. Otherwise, send augments to the player via the parcel system (requires that the Parcel System be enabled).")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
#undef RULE_CATEGORY
|
#undef RULE_CATEGORY
|
||||||
|
|||||||
+41
-16
@@ -196,6 +196,7 @@
|
|||||||
#define ServerOP_DzSaveInvite 0x0466
|
#define ServerOP_DzSaveInvite 0x0466
|
||||||
#define ServerOP_DzRequestInvite 0x0467
|
#define ServerOP_DzRequestInvite 0x0467
|
||||||
#define ServerOP_DzMakeLeader 0x0468
|
#define ServerOP_DzMakeLeader 0x0468
|
||||||
|
#define ServerOP_DzGetBulkMemberStatuses 0x0469
|
||||||
|
|
||||||
#define ServerOP_LSInfo 0x1000
|
#define ServerOP_LSInfo 0x1000
|
||||||
#define ServerOP_LSStatus 0x1001
|
#define ServerOP_LSStatus 0x1001
|
||||||
@@ -677,36 +678,53 @@ struct ServerLSPlayerZoneChange_Struct {
|
|||||||
uint32 to; // 0 = world
|
uint32 to; // 0 = world
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientAuth_Struct {
|
struct ClientAuth {
|
||||||
uint32 loginserver_account_id; // ID# in login server's db
|
uint32 loginserver_account_id; // ID# in login server's db
|
||||||
char loginserver_name[64];
|
char loginserver_name[64];
|
||||||
char account_name[30]; // username in login server's db
|
char account_name[30]; // username in login server's db
|
||||||
char key[30]; // the Key the client will present
|
char key[30]; // the key the client will present
|
||||||
uint8 lsadmin; // login server admin level
|
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
|
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;
|
uint32 ip_address;
|
||||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
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)
|
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
|
uint32 loginserver_account_id; // ID# in login server's db
|
||||||
char loginserver_account_name[30]; // username 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
|
char key[30]; // the key the client will present
|
||||||
uint8 loginserver_admin_level; // login server admin level
|
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
|
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;
|
uint32 ip_address;
|
||||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
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)
|
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,7 +852,7 @@ struct ServerSyncWorldList_Struct {
|
|||||||
bool placeholder;
|
bool placeholder;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldRequestLegacy_Struct {
|
struct UsertoWorldRequestLegacy {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
uint32 FromID;
|
uint32 FromID;
|
||||||
@@ -842,16 +860,16 @@ struct UsertoWorldRequestLegacy_Struct {
|
|||||||
char IPAddr[64];
|
char IPAddr[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldRequest_Struct {
|
struct UsertoWorldRequest {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
uint32 FromID;
|
uint32 FromID; // appears to be unused today
|
||||||
uint32 ToID;
|
uint32 ToID; // appears to be unused today
|
||||||
char IPAddr[64];
|
char IPAddr[64];
|
||||||
char login[64];
|
char login[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldResponseLegacy_Struct {
|
struct UsertoWorldResponseLegacy {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||||
@@ -859,12 +877,12 @@ struct UsertoWorldResponseLegacy_Struct {
|
|||||||
uint32 ToID;
|
uint32 ToID;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldResponse_Struct {
|
struct UsertoWorldResponse {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||||
uint32 FromID;
|
uint32 FromID; // appears to be unused today
|
||||||
uint32 ToID;
|
uint32 ToID; // appears to be unused today
|
||||||
char login[64];
|
char login[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1538,6 +1556,13 @@ struct ServerDzMemberStatuses_Struct {
|
|||||||
ServerDzMemberStatusEntry_Struct entries[0];
|
ServerDzMemberStatusEntry_Struct entries[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ServerDzCerealData_Struct {
|
||||||
|
uint16_t zone_id;
|
||||||
|
uint16_t inst_id;
|
||||||
|
uint32_t cereal_size;
|
||||||
|
char cereal_data[1];
|
||||||
|
};
|
||||||
|
|
||||||
struct ServerDzMovePC_Struct {
|
struct ServerDzMovePC_Struct {
|
||||||
uint32 dz_id;
|
uint32 dz_id;
|
||||||
uint16 sender_zone_id;
|
uint16 sender_zone_id;
|
||||||
|
|||||||
@@ -741,6 +741,7 @@ namespace BotSpellTypes
|
|||||||
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
||||||
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
||||||
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
||||||
|
constexpr uint16 PARENT_TYPE_END = BotSpellTypes::PreCombatBuffSong; // This is the last ID of the original bot spell types, the rest are considered sub types.
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<uint16, std::string> spell_type_names = {
|
static std::map<uint16, std::string> spell_type_names = {
|
||||||
|
|||||||
@@ -936,3 +936,11 @@ std::string Strings::Slugify(const std::string& input, const std::string& separa
|
|||||||
|
|
||||||
return slug;
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Strings::IsValidJson(const std::string &json)
|
||||||
|
{
|
||||||
|
rapidjson::Document doc;
|
||||||
|
rapidjson::ParseResult result = doc.Parse(json.c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <cereal/external/rapidjson/document.h>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
||||||
@@ -188,6 +189,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string Slugify(const std::string &input, const std::string &separator = "-");
|
static std::string Slugify(const std::string &input, const std::string &separator = "-");
|
||||||
|
static bool IsValidJson(const std::string& json);
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string StringFormat(const char *format, ...);
|
const std::string StringFormat(const char *format, ...);
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "23.0.2-dev" // always append -dev to the current version for custom-builds
|
#define CURRENT_VERSION "23.3.2-dev" // always append -dev to the current version for custom-builds
|
||||||
#define LOGIN_VERSION "0.8.0"
|
#define LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9306
|
#define CURRENT_BINARY_DATABASE_VERSION 9313
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
|||||||
packet.ToString()
|
packet.ToString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (packet.Length() < sizeof(UsertoWorldResponseLegacy_Struct)) {
|
if (packet.Length() < sizeof(UsertoWorldResponseLegacy)) {
|
||||||
LogError(
|
LogError(
|
||||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||||
"but was too small. Discarded to avoid buffer overrun"
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
@@ -152,7 +152,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *res = (UsertoWorldResponseLegacy_Struct *) packet.Data();
|
auto *res = (UsertoWorldResponseLegacy *) packet.Data();
|
||||||
|
|
||||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu");
|
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()
|
packet.ToString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (packet.Length() < sizeof(UsertoWorldResponse_Struct)) {
|
if (packet.Length() < sizeof(UsertoWorldResponse)) {
|
||||||
LogError(
|
LogError(
|
||||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||||
"but was too small. Discarded to avoid buffer overrun"
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
@@ -238,7 +238,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = (UsertoWorldResponse_Struct *) packet.Data();
|
auto res = (UsertoWorldResponse *) packet.Data();
|
||||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
|
|
||||||
Client *c = server.client_manager->GetClient(
|
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)
|
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
||||||
{
|
{
|
||||||
if (m_is_server_logged_in) {
|
if (m_is_server_logged_in) {
|
||||||
LogError(
|
LogInfo("Login server was already marked as logged in, returning");
|
||||||
"Login server was already marked as logged in, aborting"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HandleNewWorldserverValidation(req)) {
|
if (!HandleNewWorldserverValidation(req)) {
|
||||||
LogError("WorldServer::HandleNewWorldserver failed validation rules");
|
LogError("failed validation rules");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +492,7 @@ void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u)
|
|||||||
void WorldServer::SendClientAuthToWorld(Client *c)
|
void WorldServer::SendClientAuthToWorld(Client *c)
|
||||||
{
|
{
|
||||||
EQ::Net::DynamicPacket outapp;
|
EQ::Net::DynamicPacket outapp;
|
||||||
ClientAuth_Struct a{};
|
ClientAuth a{};
|
||||||
|
|
||||||
a.loginserver_account_id = c->GetAccountID();
|
a.loginserver_account_id = c->GetAccountID();
|
||||||
|
|
||||||
@@ -503,7 +501,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
|||||||
|
|
||||||
a.lsadmin = 0;
|
a.lsadmin = 0;
|
||||||
a.is_world_admin = 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);
|
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
||||||
|
|
||||||
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
||||||
@@ -521,7 +519,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct in_addr ip_addr{};
|
struct in_addr ip_addr{};
|
||||||
ip_addr.s_addr = a.ip;
|
ip_addr.s_addr = a.ip_address;
|
||||||
|
|
||||||
LogInfo(
|
LogInfo(
|
||||||
"Client authentication response: world_address [{}] client_address [{}]",
|
"Client authentication response: world_address [{}] client_address [{}]",
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ private:
|
|||||||
std::string m_server_version;
|
std::string m_server_version;
|
||||||
bool m_is_server_authorized_to_list;
|
bool m_is_server_authorized_to_list;
|
||||||
bool m_is_server_logged_in;
|
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);
|
static void FormatWorldServerName(char *name, int8 server_list_type);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ void WorldServerManager::SendUserLoginToWorldRequest(
|
|||||||
|
|
||||||
if (iter != m_world_servers.end()) {
|
if (iter != m_world_servers.end()) {
|
||||||
EQ::Net::DynamicPacket outapp;
|
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->worldid = server_id;
|
||||||
r->lsaccountid = client_account_id;
|
r->lsaccountid = client_account_id;
|
||||||
strncpy(r->login, client_loginserver.c_str(), 64);
|
strncpy(r->login, client_loginserver.c_str(), 64);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "23.0.2",
|
"version": "23.3.2",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -56,8 +56,9 @@ echo "# Running shared_memory"
|
|||||||
echo "# Running NPC hand-in tests"
|
echo "# Running NPC hand-in tests"
|
||||||
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
||||||
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
||||||
|
./bin/zone tests:databuckets 2>&1 | tee -a test_output.log
|
||||||
|
|
||||||
if grep -E -q "QueryErr|Error" test_output.log; then
|
if grep -E -q "QueryErr|Error|FAILED" test_output.log; then
|
||||||
echo "Error found in test output! Failing build."
|
echo "Error found in test output! Failing build."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
+13
-13
@@ -447,30 +447,30 @@ void Client::SendPostEnterWorld() {
|
|||||||
|
|
||||||
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (app->size != sizeof(LoginInfo_Struct)) {
|
if (app->size != sizeof(LoginInfo)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *login_info = (LoginInfo_Struct *) app->pBuffer;
|
auto *r = (LoginInfo *) app->pBuffer;
|
||||||
|
|
||||||
// Quagmire - max len for name is 18, pass 15
|
// Quagmire - max len for name is 18, pass 15
|
||||||
char name[19] = {0};
|
char name[19] = {0};
|
||||||
char password[16] = {0};
|
char password[16] = {0};
|
||||||
strn0cpy(name, (char *) login_info->login_info, 18);
|
strn0cpy(name, (char *) r->login_info, 18);
|
||||||
strn0cpy(password, (char *) &(login_info->login_info[strlen(name) + 1]), 15);
|
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) {
|
if (strlen(password) <= 1) {
|
||||||
LogInfo("Login without a password");
|
LogInfo("Login without a password");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_player_zoning = (login_info->zoning == 1);
|
is_player_zoning = (r->zoning == 1);
|
||||||
|
|
||||||
uint32 id = Strings::ToInt(name);
|
uint32 id = Strings::ToInt(name);
|
||||||
if (id == 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,15 +480,15 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
LogClientLogin("Checking authentication id [{}] passed", id);
|
LogClientLogin("Checking authentication id [{}] passed", id);
|
||||||
if (!is_player_zoning) {
|
if (!is_player_zoning) {
|
||||||
// Track who is in and who is out of the game
|
// 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
|
// Desktop -> Char Select
|
||||||
inout = (char *) "In";
|
in_out = "in";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Game -> Char Select
|
// Game -> Char Select
|
||||||
inout=(char *) "Out";
|
in_out = "out";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always at Char select at this point.
|
// 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.
|
// Could use a Logging Out Completely message somewhere.
|
||||||
cle->SetOnline(CLE_Status::CharSelect);
|
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 {
|
else {
|
||||||
cle->SetOnline();
|
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)) {
|
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
|
// 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.
|
// 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) {
|
if (custom_files_key != client_key) {
|
||||||
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
||||||
SendUnsupportedClientPacket(message);
|
SendUnsupportedClientPacket(message);
|
||||||
@@ -1434,7 +1434,7 @@ void Client::EnterWorld(bool TryBootup) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (TryBootup) {
|
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();
|
autobootup_timeout.Start();
|
||||||
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
||||||
if (zone_waiting_for_bootup == 0) {
|
if (zone_waiting_for_bootup == 0) {
|
||||||
|
|||||||
+155
-178
@@ -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 "../common/global_define.h"
|
||||||
#include "cliententry.h"
|
#include "cliententry.h"
|
||||||
#include "clientlist.h"
|
#include "clientlist.h"
|
||||||
@@ -24,8 +7,6 @@
|
|||||||
#include "worlddb.h"
|
#include "worlddb.h"
|
||||||
#include "zoneserver.h"
|
#include "zoneserver.h"
|
||||||
#include "world_config.h"
|
#include "world_config.h"
|
||||||
#include "../common/guilds.h"
|
|
||||||
#include "../common/strings.h"
|
|
||||||
|
|
||||||
extern uint32 numplayers;
|
extern uint32 numplayers;
|
||||||
extern LoginServerList loginserverlist;
|
extern LoginServerList loginserverlist;
|
||||||
@@ -33,84 +14,79 @@ extern ClientList client_list;
|
|||||||
extern volatile bool RunLoops;
|
extern volatile bool RunLoops;
|
||||||
extern SharedTaskManager shared_task_manager;
|
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(
|
ClientListEntry::ClientListEntry(
|
||||||
uint32 in_id,
|
uint32 id,
|
||||||
uint32 in_loginserver_id,
|
uint32 login_server_id,
|
||||||
const char *in_loginserver_name,
|
const char *login_server_name,
|
||||||
const char *in_login_name,
|
const char *account_name,
|
||||||
const char *in_login_key,
|
const char *login_key,
|
||||||
int16 in_is_world_admin,
|
int16 is_world_admin,
|
||||||
uint32 ip,
|
uint32 ip_address,
|
||||||
uint8 local
|
uint8 local
|
||||||
)
|
)
|
||||||
: id(in_id)
|
: m_id(id)
|
||||||
{
|
{
|
||||||
ClearVars(true);
|
ClearVars(true);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"in_id [{0}] in_loginserver_id [{1}] in_loginserver_name [{2}] in_login_name [{3}] in_login_key [{4}] "
|
"id [{}] loginserver_id [{}] loginserver_name [{}] login_name [{}] login_key [{}] is_world_admin [{}] ip [{}] local [{}]",
|
||||||
" in_is_world_admin [{5}] ip [{6}] local [{7}]",
|
id,
|
||||||
in_id,
|
login_server_id,
|
||||||
in_loginserver_id,
|
login_server_name,
|
||||||
in_loginserver_name,
|
account_name,
|
||||||
in_login_name,
|
login_key,
|
||||||
in_login_key,
|
is_world_admin,
|
||||||
in_is_world_admin,
|
ip_address,
|
||||||
ip,
|
|
||||||
local
|
local
|
||||||
);
|
);
|
||||||
|
|
||||||
pIP = ip;
|
m_ip_address = ip_address;
|
||||||
pLSID = in_loginserver_id;
|
m_login_server_id = login_server_id;
|
||||||
if (in_loginserver_id > 0) {
|
if (login_server_id > 0) {
|
||||||
paccountid = database.GetAccountIDFromLSID(in_loginserver_name, in_loginserver_id, paccountname, &padmin);
|
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(m_login_account_name, account_name, sizeof(m_login_account_name));
|
||||||
strn0cpy(plskey, in_login_key, sizeof(plskey));
|
strn0cpy(m_key, login_key, sizeof(m_key));
|
||||||
strn0cpy(source_loginserver, in_loginserver_name, sizeof(source_loginserver));
|
strn0cpy(m_source_loginserver, login_server_name, sizeof(m_source_loginserver));
|
||||||
pworldadmin = in_is_world_admin;
|
|
||||||
plocal = (local == 1);
|
|
||||||
|
|
||||||
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)
|
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online)
|
||||||
: id(in_id)
|
: m_id(in_id)
|
||||||
{
|
{
|
||||||
ClearVars(true);
|
ClearVars(true);
|
||||||
|
|
||||||
pIP = 0;
|
m_ip_address = 0;
|
||||||
pLSID = scl->LSAccountID;
|
m_login_server_id = scl->LSAccountID;
|
||||||
strn0cpy(loginserver_account_name, scl->name, sizeof(loginserver_account_name));
|
strn0cpy(m_login_account_name, scl->name, sizeof(m_login_account_name));
|
||||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||||
pworldadmin = 0;
|
m_world_admin = 0;
|
||||||
|
|
||||||
paccountid = scl->AccountID;
|
m_account_id = scl->AccountID;
|
||||||
strn0cpy(paccountname, scl->AccountName, sizeof(paccountname));
|
strn0cpy(m_account_name, scl->AccountName, sizeof(m_account_name));
|
||||||
padmin = scl->Admin;
|
m_admin = scl->Admin;
|
||||||
|
|
||||||
pinstance = 0;
|
m_instance = 0;
|
||||||
pLFGFromLevel = 0;
|
m_lfg_from_level = 0;
|
||||||
pLFGToLevel = 0;
|
m_lfg_to_level = 0;
|
||||||
pLFGMatchFilter = false;
|
m_lfg_match_filter = false;
|
||||||
memset(pLFGComments, 0, 64);
|
memset(m_lfg_comments, 0, 64);
|
||||||
|
|
||||||
if (iOnline >= CLE_Status::Zoning) {
|
if (online >= CLE_Status::Zoning) {
|
||||||
Update(iZS, scl, iOnline);
|
Update(z, scl, online);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SetOnline(iOnline);
|
SetOnline(online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,16 +96,16 @@ ClientListEntry::~ClientListEntry()
|
|||||||
Camp(); // updates zoneserver's numplayers
|
Camp(); // updates zoneserver's numplayers
|
||||||
client_list.RemoveCLEReferances(this);
|
client_list.RemoveCLEReferances(this);
|
||||||
}
|
}
|
||||||
for (auto& elem : tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
tell_queue.clear();
|
m_tell_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
||||||
{
|
{
|
||||||
pcharid = iCharID;
|
m_char_id = iCharID;
|
||||||
strn0cpy(pname, iCharName, sizeof(pname));
|
strn0cpy(m_char_name, iCharName, sizeof(m_char_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
||||||
@@ -142,20 +118,20 @@ void ClientListEntry::SetOnline(CLE_Status iOnline)
|
|||||||
static_cast<int>(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++;
|
numplayers++;
|
||||||
}
|
}
|
||||||
else if (iOnline < CLE_Status::Online && pOnline >= CLE_Status::Online) {
|
else if (iOnline < CLE_Status::Online && m_online >= CLE_Status::Online) {
|
||||||
numplayers--;
|
numplayers--;
|
||||||
}
|
}
|
||||||
if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) {
|
if (iOnline != CLE_Status::Online || m_online < CLE_Status::Online) {
|
||||||
pOnline = iOnline;
|
m_online = iOnline;
|
||||||
}
|
}
|
||||||
if (iOnline < CLE_Status::Zoning) {
|
if (iOnline < CLE_Status::Zoning) {
|
||||||
Camp();
|
Camp();
|
||||||
}
|
}
|
||||||
if (pOnline >= CLE_Status::Online) {
|
if (m_online >= CLE_Status::Online) {
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,49 +169,49 @@ void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz)
|
|||||||
|
|
||||||
void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
||||||
{
|
{
|
||||||
if (pzoneserver != iZS) {
|
if (m_zone_server != iZS) {
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
if (iZS) {
|
if (iZS) {
|
||||||
iZS->AddPlayer();
|
iZS->AddPlayer();
|
||||||
LSUpdate(iZS);
|
LSUpdate(iZS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pzoneserver = iZS;
|
m_zone_server = iZS;
|
||||||
pzone = scl->zone;
|
m_zone = scl->zone;
|
||||||
pinstance = scl->instance_id;
|
m_instance = scl->instance_id;
|
||||||
pcharid = scl->charid;
|
m_char_id = scl->charid;
|
||||||
|
|
||||||
strcpy(pname, scl->name);
|
strcpy(m_char_name, scl->name);
|
||||||
if (paccountid == 0) {
|
if (m_account_id == 0) {
|
||||||
paccountid = scl->AccountID;
|
m_account_id = scl->AccountID;
|
||||||
strcpy(paccountname, scl->AccountName);
|
strcpy(m_account_name, scl->AccountName);
|
||||||
strcpy(loginserver_account_name, scl->AccountName);
|
strcpy(m_login_account_name, scl->AccountName);
|
||||||
pIP = scl->IP;
|
m_ip_address = scl->IP;
|
||||||
pLSID = scl->LSAccountID;
|
m_login_server_id = scl->LSAccountID;
|
||||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||||
}
|
}
|
||||||
padmin = scl->Admin;
|
m_admin = scl->Admin;
|
||||||
plevel = scl->level;
|
m_level = scl->level;
|
||||||
pclass_ = scl->class_;
|
m_class_ = scl->class_;
|
||||||
prace = scl->race;
|
m_race = scl->race;
|
||||||
panon = scl->anon;
|
m_anon = scl->anon;
|
||||||
ptellsoff = scl->tellsoff;
|
m_tells_off = scl->tellsoff;
|
||||||
pguild_id = scl->guild_id;
|
m_guild_id = scl->guild_id;
|
||||||
pguild_rank = scl->guild_rank;
|
m_guild_rank = scl->guild_rank;
|
||||||
pguild_tribute_opt_in = scl->guild_tribute_opt_in;
|
m_guild_tribute_opt_in = scl->guild_tribute_opt_in;
|
||||||
pLFG = scl->LFG;
|
m_lfg = scl->LFG;
|
||||||
gm = scl->gm;
|
m_gm = scl->gm;
|
||||||
pClientVersion = scl->ClientVersion;
|
m_client_version = scl->ClientVersion;
|
||||||
|
|
||||||
// Fields from the LFG Window
|
// Fields from the LFG Window
|
||||||
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
||||||
pLFGFromLevel = scl->LFGFromLevel;
|
m_lfg_from_level = scl->LFGFromLevel;
|
||||||
pLFGToLevel = scl->LFGToLevel;
|
m_lfg_to_level = scl->LFGToLevel;
|
||||||
pLFGMatchFilter = scl->LFGMatchFilter;
|
m_lfg_match_filter = scl->LFGMatchFilter;
|
||||||
memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments));
|
memcpy(m_lfg_comments, scl->LFGComments, sizeof(m_lfg_comments));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
@@ -243,76 +219,76 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_
|
|||||||
|
|
||||||
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
||||||
{
|
{
|
||||||
if (iZS != 0 && iZS != pzoneserver) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
|
|
||||||
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
||||||
|
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
pzoneserver = 0;
|
m_zone_server = 0;
|
||||||
pzone = 0;
|
m_zone = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::ClearVars(bool iAll)
|
void ClientListEntry::ClearVars(bool iAll)
|
||||||
{
|
{
|
||||||
if (iAll) {
|
if (iAll) {
|
||||||
pOnline = CLE_Status::Never;
|
m_online = CLE_Status::Never;
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
|
|
||||||
pLSID = 0;
|
m_login_server_id = 0;
|
||||||
memset(loginserver_account_name, 0, sizeof(loginserver_account_name));
|
memset(m_login_account_name, 0, sizeof(m_login_account_name));
|
||||||
memset(plskey, 0, sizeof(plskey));
|
memset(m_key, 0, sizeof(m_key));
|
||||||
pworldadmin = 0;
|
m_world_admin = 0;
|
||||||
|
|
||||||
paccountid = 0;
|
m_account_id = 0;
|
||||||
memset(paccountname, 0, sizeof(paccountname));
|
memset(m_account_name, 0, sizeof(m_account_name));
|
||||||
padmin = AccountStatus::Player;
|
m_admin = AccountStatus::Player;
|
||||||
}
|
}
|
||||||
pzoneserver = 0;
|
m_zone_server = 0;
|
||||||
pzone = 0;
|
m_zone = 0;
|
||||||
pcharid = 0;
|
m_char_id = 0;
|
||||||
memset(pname, 0, sizeof(pname));
|
memset(m_char_name, 0, sizeof(m_char_name));
|
||||||
plevel = 0;
|
m_level = 0;
|
||||||
pclass_ = 0;
|
m_class_ = 0;
|
||||||
prace = 0;
|
m_race = 0;
|
||||||
panon = 0;
|
m_anon = 0;
|
||||||
ptellsoff = 0;
|
m_tells_off = 0;
|
||||||
pguild_id = GUILD_NONE;
|
m_guild_id = GUILD_NONE;
|
||||||
pguild_rank = 0;
|
m_guild_rank = 0;
|
||||||
pLFG = 0;
|
m_lfg = 0;
|
||||||
gm = 0;
|
m_gm = 0;
|
||||||
pClientVersion = 0;
|
m_client_version = 0;
|
||||||
for (auto& elem : tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
tell_queue.clear();
|
m_tell_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::Camp(ZoneServer *iZS)
|
void ClientListEntry::Camp(ZoneServer *iZS)
|
||||||
{
|
{
|
||||||
if (iZS != 0 && iZS != pzoneserver) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearVars();
|
ClearVars();
|
||||||
|
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientListEntry::CheckStale()
|
bool ClientListEntry::CheckStale()
|
||||||
{
|
{
|
||||||
stale++;
|
m_stale++;
|
||||||
if (stale > 20) {
|
if (m_stale > 20) {
|
||||||
if (pOnline > CLE_Status::Offline) {
|
if (m_online > CLE_Status::Offline) {
|
||||||
SetOnline(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)
|
bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password)
|
||||||
{
|
{
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}]",
|
"ls_account_id [{}] key_password [{}] key [{}]",
|
||||||
loginserver_account_id,
|
loginserver_account_id,
|
||||||
key_password,
|
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(
|
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,
|
loginserver_account_id,
|
||||||
key_password,
|
key_password,
|
||||||
plskey,
|
m_key,
|
||||||
LSID(),
|
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;
|
int16 default_account_status = WorldConfig::get()->DefaultStatus;
|
||||||
|
|
||||||
paccountid = database.CreateAccount(
|
m_account_id = database.CreateAccount(
|
||||||
loginserver_account_name,
|
m_login_account_name,
|
||||||
std::string(),
|
std::string(),
|
||||||
default_account_status,
|
default_account_status,
|
||||||
source_loginserver,
|
m_source_loginserver,
|
||||||
LSID()
|
LSID()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!paccountid) {
|
if (!m_account_id) {
|
||||||
LogInfo(
|
LogError(
|
||||||
"Error adding local account for LS login: [{0}:{1}], duplicate name",
|
"Error adding local account for LS login [{}] [{}], duplicate name",
|
||||||
source_loginserver,
|
m_source_loginserver,
|
||||||
loginserver_account_name
|
m_login_account_name
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
strn0cpy(paccountname, loginserver_account_name, sizeof(paccountname));
|
strn0cpy(m_account_name, m_login_account_name, sizeof(m_account_name));
|
||||||
padmin = default_account_status;
|
m_admin = default_account_status;
|
||||||
}
|
}
|
||||||
std::string lsworldadmin;
|
std::string lsworldadmin;
|
||||||
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
||||||
if (Strings::ToInt(lsworldadmin) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == AccountStatus::Player)) {
|
if (Strings::ToInt(lsworldadmin) == 1 && m_world_admin != 0 &&
|
||||||
padmin = pworldadmin;
|
(m_admin < m_world_admin || m_admin == AccountStatus::Player)) {
|
||||||
|
m_admin = m_world_admin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -380,8 +358,8 @@ void ClientListEntry::ProcessTellQueue()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerPacket *pack;
|
ServerPacket *pack;
|
||||||
auto it = tell_queue.begin();
|
auto it = m_tell_queue.begin();
|
||||||
while (it != tell_queue.end()) {
|
while (it != m_tell_queue.end()) {
|
||||||
pack = new ServerPacket(
|
pack = new ServerPacket(
|
||||||
ServerOP_ChannelMessage,
|
ServerOP_ChannelMessage,
|
||||||
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
||||||
@@ -390,8 +368,7 @@ void ClientListEntry::ProcessTellQueue()
|
|||||||
Server()->SendPacket(pack);
|
Server()->SendPacket(pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
safe_delete_array(*it);
|
safe_delete_array(*it);
|
||||||
it = tell_queue.erase(it);
|
it = m_tell_queue.erase(it);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+88
-106
@@ -8,8 +8,7 @@
|
|||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
|
||||||
Never,
|
Never,
|
||||||
Offline,
|
Offline,
|
||||||
Online,
|
Online,
|
||||||
@@ -33,35 +32,18 @@ struct ServerClientList_Struct;
|
|||||||
class ClientListEntry {
|
class ClientListEntry {
|
||||||
public:
|
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(
|
ClientListEntry(
|
||||||
uint32 id,
|
uint32 id,
|
||||||
uint32 in_loginserver_id,
|
uint32 login_server_id,
|
||||||
const char *in_loginserver_name,
|
const char *login_server_name,
|
||||||
const char *in_login_name,
|
const char *account_name,
|
||||||
const char *in_login_key,
|
const char *login_key,
|
||||||
int16 in_is_world_admin = 0,
|
int16 is_world_admin = 0,
|
||||||
uint32 ip = 0,
|
uint32 ip_address = 0,
|
||||||
uint8 local = 0
|
uint8 local = 0
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
ClientListEntry(uint32 id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online);
|
||||||
* @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();
|
~ClientListEntry();
|
||||||
bool CheckStale();
|
bool CheckStale();
|
||||||
void Update(ZoneServer *zoneserver, ServerClientList_Struct *scl, CLE_Status iOnline = CLE_Status::InZone);
|
void Update(ZoneServer *zoneserver, ServerClientList_Struct *scl, CLE_Status iOnline = CLE_Status::InZone);
|
||||||
@@ -70,61 +52,61 @@ public:
|
|||||||
bool CheckAuth(uint32 loginserver_account_id, const char *key_password);
|
bool CheckAuth(uint32 loginserver_account_id, const char *key_password);
|
||||||
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
||||||
void SetChar(uint32 iCharID, const char *iCharName);
|
void SetChar(uint32 iCharID, const char *iCharName);
|
||||||
inline CLE_Status Online() { return pOnline; }
|
inline CLE_Status Online() { return m_online; }
|
||||||
inline const uint32 GetID() const { return id; }
|
inline const uint32 GetID() const { return m_id; }
|
||||||
inline const uint32 GetIP() const { return pIP; }
|
inline const uint32 GetIP() const { return m_ip_address; }
|
||||||
inline void SetIP(const uint32& iIP) { pIP = iIP; }
|
inline void SetIP(const uint32 &iIP) { m_ip_address = iIP; }
|
||||||
inline void KeepAlive() { stale = 0; }
|
inline void KeepAlive() { m_stale = 0; }
|
||||||
inline uint8 GetStaleCounter() const { return stale; }
|
inline uint8 GetStaleCounter() const { return m_stale; }
|
||||||
void LeavingZone(ZoneServer *iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
void LeavingZone(ZoneServer *iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
||||||
void Camp(ZoneServer *iZS = 0);
|
void Camp(ZoneServer *iZS = 0);
|
||||||
|
|
||||||
// Login Server stuff
|
// Login Server stuff
|
||||||
inline const char* LoginServer() const { return source_loginserver; }
|
inline const char *LoginServer() const { return m_source_loginserver; }
|
||||||
inline uint32 LSID() const { return pLSID; }
|
inline uint32 LSID() const { return m_login_server_id; }
|
||||||
inline uint32 LSAccountID() const { return pLSID; }
|
inline uint32 LSAccountID() const { return m_login_server_id; }
|
||||||
inline const char* LSName() const { return loginserver_account_name; }
|
inline const char *LSName() const { return m_login_account_name; }
|
||||||
inline int16 WorldAdmin() const { return pworldadmin; }
|
inline int16 WorldAdmin() const { return m_world_admin; }
|
||||||
inline const char* GetLSKey() const { return plskey; }
|
inline const char *GetLSKey() const { return m_key; }
|
||||||
inline const CLE_Status GetOnline() const { return pOnline; }
|
inline const CLE_Status GetOnline() const { return m_online; }
|
||||||
|
|
||||||
// Account stuff
|
// Account stuff
|
||||||
inline uint32 AccountID() const { return paccountid; }
|
inline uint32 AccountID() const { return m_account_id; }
|
||||||
inline const char* AccountName() const { return paccountname; }
|
inline const char *AccountName() const { return m_account_name; }
|
||||||
inline int16 Admin() const { return padmin; }
|
inline int16 Admin() const { return m_admin; }
|
||||||
inline void SetAdmin(uint16 iAdmin) { padmin = iAdmin; }
|
inline void SetAdmin(uint16 iAdmin) { m_admin = iAdmin; }
|
||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
inline ZoneServer *Server() const { return pzoneserver; }
|
inline ZoneServer *Server() const { return m_zone_server; }
|
||||||
inline void ClearServer() { pzoneserver = 0; }
|
inline void ClearServer() { m_zone_server = 0; }
|
||||||
inline uint32 CharID() const { return pcharid; }
|
inline uint32 CharID() const { return m_char_id; }
|
||||||
inline const char *name() const { return pname; }
|
inline const char *name() const { return m_char_name; }
|
||||||
inline uint32 zone() const { return pzone; }
|
inline uint32 zone() const { return m_zone; }
|
||||||
inline uint16 instance() const { return pinstance; }
|
inline uint16 instance() const { return m_instance; }
|
||||||
inline uint8 level() const { return plevel; }
|
inline uint8 level() const { return m_level; }
|
||||||
inline uint8 class_() const { return pclass_; }
|
inline uint8 class_() const { return m_class_; }
|
||||||
inline uint16 race() const { return prace; }
|
inline uint16 race() const { return m_race; }
|
||||||
inline uint8 Anon() { return panon; }
|
inline uint8 Anon() { return m_anon; }
|
||||||
inline uint8 TellsOff() const { return ptellsoff; }
|
inline uint8 TellsOff() const { return m_tells_off; }
|
||||||
inline uint32 GuildID() const { return pguild_id; }
|
inline uint32 GuildID() const { return m_guild_id; }
|
||||||
inline uint32 GuildRank() const { return pguild_rank; }
|
inline uint32 GuildRank() const { return m_guild_rank; }
|
||||||
inline bool GuildTributeOptIn() const { return pguild_tribute_opt_in; }
|
inline bool GuildTributeOptIn() const { return m_guild_tribute_opt_in; }
|
||||||
inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; }
|
inline void SetGuild(uint32 guild_id) { m_guild_id = guild_id; }
|
||||||
inline void SetGuildTributeOptIn(bool opt) { pguild_tribute_opt_in = opt; }
|
inline void SetGuildTributeOptIn(bool opt) { m_guild_tribute_opt_in = opt; }
|
||||||
inline bool LFG() const { return pLFG; }
|
inline bool LFG() const { return m_lfg; }
|
||||||
inline uint8 GetGM() const { return gm; }
|
inline uint8 GetGM() const { return m_gm; }
|
||||||
inline void SetGM(uint8 igm) { gm = igm; }
|
inline void SetGM(uint8 igm) { m_gm = igm; }
|
||||||
inline void SetZone(uint32 zone) { pzone = zone; }
|
inline void SetZone(uint32 zone) { m_zone = zone; }
|
||||||
inline bool IsLocalClient() const { return plocal; }
|
inline bool IsLocalClient() const { return m_is_local; }
|
||||||
inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; }
|
inline uint8 GetLFGFromLevel() const { return m_lfg_from_level; }
|
||||||
inline uint8 GetLFGToLevel() const { return pLFGToLevel; }
|
inline uint8 GetLFGToLevel() const { return m_lfg_to_level; }
|
||||||
inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; }
|
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
|
||||||
inline const char *GetLFGComments() const { return pLFGComments; }
|
inline const char *GetLFGComments() const { return m_lfg_comments; }
|
||||||
inline uint8 GetClientVersion() { return pClientVersion; }
|
inline uint8 GetClientVersion() { return m_client_version; }
|
||||||
|
|
||||||
inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); }
|
inline bool TellQueueFull() const { return m_tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||||
inline bool TellQueueEmpty() const { return tell_queue.empty(); }
|
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
|
||||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
|
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { m_tell_queue.push_back(scm); }
|
||||||
void ProcessTellQueue();
|
void ProcessTellQueue();
|
||||||
|
|
||||||
void SetPendingDzInvite(ServerPacket *pack) { m_dz_invite.reset(pack->Copy()); };
|
void SetPendingDzInvite(ServerPacket *pack) { m_dz_invite.reset(pack->Copy()); };
|
||||||
@@ -133,48 +115,48 @@ public:
|
|||||||
private:
|
private:
|
||||||
void ClearVars(bool iAll = false);
|
void ClearVars(bool iAll = false);
|
||||||
|
|
||||||
const uint32 id;
|
const uint32 m_id;
|
||||||
uint32 pIP;
|
uint32 m_ip_address;
|
||||||
CLE_Status pOnline;
|
CLE_Status m_online;
|
||||||
uint8 stale;
|
uint8 m_stale;
|
||||||
|
|
||||||
// Login Server stuff
|
// Login Server stuff
|
||||||
char source_loginserver[64]{}; //Loginserver we came from.
|
char m_source_loginserver[64]{}; //Loginserver we came from.
|
||||||
uint32 pLSID;
|
uint32 m_login_server_id;
|
||||||
char loginserver_account_name[32]{};
|
char m_login_account_name[32]{};
|
||||||
char plskey[16]{};
|
char m_key[16]{};
|
||||||
int16 pworldadmin; // Login server's suggested admin status setting
|
int16 m_world_admin; // Login server's suggested admin status setting
|
||||||
bool plocal;
|
bool m_is_local;
|
||||||
|
|
||||||
// Account stuff
|
// Account stuff
|
||||||
uint32 paccountid;
|
uint32 m_account_id;
|
||||||
char paccountname[32]{};
|
char m_account_name[32]{};
|
||||||
int16 padmin{};
|
int16 m_admin{};
|
||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
ZoneServer* pzoneserver{};
|
ZoneServer *m_zone_server{};
|
||||||
uint32 pzone{};
|
uint32 m_zone{};
|
||||||
uint16 pinstance{};
|
uint16 m_instance{};
|
||||||
uint32 pcharid{};
|
uint32 m_char_id{};
|
||||||
char pname[64]{};
|
char m_char_name[64]{};
|
||||||
uint8 plevel{};
|
uint8 m_level{};
|
||||||
uint8 pclass_{};
|
uint8 m_class_{};
|
||||||
uint16 prace{};
|
uint16 m_race{};
|
||||||
uint8 panon{};
|
uint8 m_anon{};
|
||||||
uint8 ptellsoff{};
|
uint8 m_tells_off{};
|
||||||
uint32 pguild_id{};
|
uint32 m_guild_id{};
|
||||||
uint32 pguild_rank;
|
uint32 m_guild_rank;
|
||||||
bool pguild_tribute_opt_in{};
|
bool m_guild_tribute_opt_in{};
|
||||||
bool pLFG{};
|
bool m_lfg{};
|
||||||
uint8 gm{};
|
uint8 m_gm{};
|
||||||
uint8 pClientVersion{};
|
uint8 m_client_version{};
|
||||||
uint8 pLFGFromLevel{};
|
uint8 m_lfg_from_level{};
|
||||||
uint8 pLFGToLevel{};
|
uint8 m_lfg_to_level{};
|
||||||
bool pLFGMatchFilter{};
|
bool m_lfg_match_filter{};
|
||||||
char pLFGComments[64]{};
|
char m_lfg_comments[64]{};
|
||||||
|
|
||||||
// Tell Queue -- really a vector :D
|
// 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;
|
std::unique_ptr<ServerPacket> m_dz_invite;
|
||||||
};
|
};
|
||||||
|
|||||||
+23
-5
@@ -331,8 +331,26 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClientList::CLEAdd(uint32 iLSID, const char *iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) {
|
void ClientList::CLEAdd(
|
||||||
auto tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginServerName, iLoginName, iLoginKey, iWorldAdmin, ip, local);
|
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);
|
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);
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
iterator.Reset();
|
iterator.Reset();
|
||||||
while (iterator.MoreElements()) {
|
while (iterator.MoreElements()) {
|
||||||
if (iterator.GetData()->CheckAuth(iLSID, iKey)) {
|
if (iterator.GetData()->CheckAuth(loginserver_account_id, key)) {
|
||||||
return iterator.GetData();
|
return iterator.GetData();
|
||||||
}
|
}
|
||||||
iterator.Advance();
|
iterator.Advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
||||||
|
|||||||
+2
-2
@@ -51,7 +51,7 @@ public:
|
|||||||
|
|
||||||
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
||||||
void CLERemoveZSRef(ZoneServer* iZS);
|
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* FindCharacter(const char* name);
|
||||||
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
||||||
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
void DisconnectByIP(uint32 in_ip);
|
void DisconnectByIP(uint32 in_ip);
|
||||||
void CLCheckStale();
|
void CLCheckStale();
|
||||||
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
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);
|
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
||||||
bool IsAccountInGame(uint32 iLSID);
|
bool IsAccountInGame(uint32 iLSID);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "zoneserver.h"
|
#include "zoneserver.h"
|
||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
||||||
|
#include <cereal/types/utility.hpp>
|
||||||
|
|
||||||
extern ClientList client_list;
|
extern ClientList client_list;
|
||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
@@ -169,6 +170,33 @@ void DynamicZoneManager::LoadTemplates()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DynamicZoneManager::SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint32_t, std::vector<DynamicZoneMember>>> dzs;
|
||||||
|
dzs.reserve(dynamic_zone_cache.size());
|
||||||
|
|
||||||
|
for (const auto& [dz_id, dz] : dynamic_zone_cache)
|
||||||
|
{
|
||||||
|
dzs.emplace_back(dz_id, dz->GetMembers());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
{
|
||||||
|
cereal::BinaryOutputArchive archive(ss);
|
||||||
|
archive(dzs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view sv = ss.view();
|
||||||
|
|
||||||
|
size_t size = sizeof(ServerDzCerealData_Struct) + sv.size();
|
||||||
|
ServerPacket pack(ServerOP_DzGetBulkMemberStatuses, static_cast<uint32_t>(size));
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack.pBuffer);
|
||||||
|
buf->cereal_size = static_cast<uint32_t>(sv.size());
|
||||||
|
memcpy(buf->cereal_data, sv.data(), sv.size());
|
||||||
|
|
||||||
|
zoneserver_list.SendPacket(zone_id, inst_id, &pack);
|
||||||
|
}
|
||||||
|
|
||||||
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
||||||
{
|
{
|
||||||
switch (pack->opcode)
|
switch (pack->opcode)
|
||||||
@@ -338,6 +366,15 @@ void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
|
{
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack->pBuffer);
|
||||||
|
if (buf->zone_id != 0 && !dynamic_zone_cache.empty())
|
||||||
|
{
|
||||||
|
SendBulkMemberStatuses(buf->zone_id, buf->inst_id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ServerOP_DzUpdateMemberStatus:
|
case ServerOP_DzUpdateMemberStatus:
|
||||||
{
|
{
|
||||||
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public:
|
|||||||
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
|
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id);
|
||||||
|
|
||||||
Timer m_process_throttle_timer{};
|
Timer m_process_throttle_timer{};
|
||||||
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
|
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
|
||||||
};
|
};
|
||||||
|
|||||||
+38
-38
@@ -46,7 +46,7 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequestLegacy_Struct *utwr = (UsertoWorldRequestLegacy_Struct *) p.Data();
|
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id);
|
||||||
|
|
||||||
@@ -63,11 +63,11 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
|
|
||||||
ServerPacket outpack;
|
ServerPacket outpack;
|
||||||
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
||||||
outpack.size = sizeof(UsertoWorldResponseLegacy_Struct);
|
outpack.size = sizeof(UsertoWorldResponseLegacy);
|
||||||
outpack.pBuffer = new uchar[outpack.size];
|
outpack.pBuffer = new uchar[outpack.size];
|
||||||
memset(outpack.pBuffer, 0, 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->lsaccountid = utwr->lsaccountid;
|
||||||
utwrs->ToID = utwr->FromID;
|
utwrs->ToID = utwr->FromID;
|
||||||
utwrs->worldid = utwr->worldid;
|
utwrs->worldid = utwr->worldid;
|
||||||
@@ -126,7 +126,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct *) p.Data();
|
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id);
|
||||||
|
|
||||||
@@ -143,11 +143,11 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
|
|
||||||
ServerPacket outpack;
|
ServerPacket outpack;
|
||||||
outpack.opcode = ServerOP_UsertoWorldResp;
|
outpack.opcode = ServerOP_UsertoWorldResp;
|
||||||
outpack.size = sizeof(UsertoWorldResponse_Struct);
|
outpack.size = sizeof(UsertoWorldResponse);
|
||||||
outpack.pBuffer = new uchar[outpack.size];
|
outpack.pBuffer = new uchar[outpack.size];
|
||||||
memset(outpack.pBuffer, 0, 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->lsaccountid = utwr->lsaccountid;
|
||||||
utwrs->ToID = utwr->FromID;
|
utwrs->ToID = utwr->FromID;
|
||||||
strn0cpy(utwrs->login, utwr->login, 64);
|
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);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client_authentication_request = p.GetSerialize<ClientAuthLegacy_Struct>(0);
|
auto r = p.GetSerialize<ClientAuthLegacy>(0);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Processing Loginserver Auth Legacy | account_id [{0}] account_name [{1}] key [{2}] admin [{3}] ip [{4}] "
|
"Processing Loginserver Auth Legacy | account_id [{}] account_name [{}] key [{}] admin [{}] ip [{}] "
|
||||||
"local_network [{5}]",
|
"local_network [{}]",
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.loginserver_account_name,
|
r.loginserver_account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
|
|
||||||
client_list.CLEAdd(
|
client_list.CLEAdd(
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
"eqemu",
|
"eqemu",
|
||||||
client_authentication_request.loginserver_account_name,
|
r.loginserver_account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (std::exception &ex) {
|
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);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client_authentication_request = p.GetSerialize<ClientAuth_Struct>(0);
|
auto r = p.GetSerialize<ClientAuth>(0);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Processing Loginserver Auth | account_id [{0}] account_name [{1}] loginserver_name [{2}] key [{3}] "
|
"Processing Loginserver Auth | account_id [{}] account_name [{}] loginserver_name [{}] key [{}] "
|
||||||
"admin [{4}] ip [{5}] local_network [{6}]",
|
"admin [{}] ip [{}] local_network [{}]",
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.account_name,
|
r.account_name,
|
||||||
client_authentication_request.loginserver_name,
|
r.loginserver_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
|
|
||||||
client_list.CLEAdd(
|
client_list.CLEAdd(
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.loginserver_name,
|
r.loginserver_name,
|
||||||
client_authentication_request.account_name,
|
r.account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (std::exception &ex) {
|
catch (std::exception &ex) {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
#include "../common/events/player_event_logs.h"
|
#include "../common/events/player_event_logs.h"
|
||||||
#include "../common/skill_caps.h"
|
#include "../common/skill_caps.h"
|
||||||
#include "../common/repositories/character_parcels_repository.h"
|
#include "../common/repositories/character_parcels_repository.h"
|
||||||
|
#include "../common/ip_util.h"
|
||||||
|
|
||||||
SkillCaps skill_caps;
|
SkillCaps skill_caps;
|
||||||
ZoneStore zone_store;
|
ZoneStore zone_store;
|
||||||
@@ -188,6 +189,11 @@ int main(int argc, char **argv)
|
|||||||
launcher_list.LoadList();
|
launcher_list.LoadList();
|
||||||
zoneserver_list.Init();
|
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;
|
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
||||||
if (Config->TelnetEnabled) {
|
if (Config->TelnetEnabled) {
|
||||||
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
||||||
|
|||||||
@@ -101,6 +101,13 @@ bool WorldBoot::HandleCommandInput(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if we ran a valid command, this whole CLI handler needs to be improved at a later time
|
||||||
|
std::string arg1 = argc >= 2 ? argv[1] : "";
|
||||||
|
if (argc >= 2 && !Strings::Contains(arg1, ":")) {
|
||||||
|
std::cout << "Invalid command, use --help to see available commands" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,6 @@ WorldDatabase content_db;
|
|||||||
extern std::vector<RaceClassAllocation> character_create_allocations;
|
extern std::vector<RaceClassAllocation> character_create_allocations;
|
||||||
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
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)
|
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
||||||
{
|
{
|
||||||
EQ::versions::ClientVersion
|
EQ::versions::ClientVersion
|
||||||
|
|||||||
+2
-1
@@ -894,7 +894,8 @@ void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
|||||||
ServerReload::Type::Commands,
|
ServerReload::Type::Commands,
|
||||||
ServerReload::Type::PerlExportSettings,
|
ServerReload::Type::PerlExportSettings,
|
||||||
ServerReload::Type::DataBucketsCache,
|
ServerReload::Type::DataBucketsCache,
|
||||||
ServerReload::Type::WorldRepop
|
ServerReload::Type::WorldRepop,
|
||||||
|
ServerReload::Type::WorldWithRespawn
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set requires_zone_booted flag before executing reload logic
|
// Set requires_zone_booted flag before executing reload logic
|
||||||
|
|||||||
@@ -1480,6 +1480,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
case ServerOP_DzSwapMembers:
|
case ServerOP_DzSwapMembers:
|
||||||
case ServerOP_DzRemoveAllMembers:
|
case ServerOP_DzRemoveAllMembers:
|
||||||
case ServerOP_DzGetMemberStatuses:
|
case ServerOP_DzGetMemberStatuses:
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
case ServerOP_DzSetSecondsRemaining:
|
case ServerOP_DzSetSecondsRemaining:
|
||||||
case ServerOP_DzSetCompass:
|
case ServerOP_DzSetCompass:
|
||||||
case ServerOP_DzSetSafeReturn:
|
case ServerOP_DzSetSafeReturn:
|
||||||
|
|||||||
@@ -2789,6 +2789,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
const uint16 entity_id = GetID();
|
const uint16 entity_id = GetID();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
(
|
||||||
!HasOwner() &&
|
!HasOwner() &&
|
||||||
!IsMerc() &&
|
!IsMerc() &&
|
||||||
!GetSwarmInfo() &&
|
!GetSwarmInfo() &&
|
||||||
@@ -2814,6 +2815,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
killer_mob && is_ldon_treasure
|
killer_mob && is_ldon_treasure
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|| IsQueuedForCorpse()
|
||||||
) {
|
) {
|
||||||
if (killer) {
|
if (killer) {
|
||||||
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
||||||
|
|||||||
+5
-1
@@ -690,12 +690,16 @@ void Aura::ProcessSpawns()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e.second->IsOfClientBot()) {
|
if (!e.second->IsClient()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto c = e.second->CastToClient();
|
auto c = e.second->CastToClient();
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
|
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
|
||||||
if (ShouldISpawnFor(c)) {
|
if (ShouldISpawnFor(c)) {
|
||||||
if (!spawned) {
|
if (!spawned) {
|
||||||
|
|||||||
+70
-280
@@ -281,31 +281,42 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_ammo_item) {
|
if (is_ammo_item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
||||||
|
const bool meets_recommended = (IsNPC() && !RuleB(Items, NPCUseRecommendedLevels)) || current_level >= recommended_level;
|
||||||
|
|
||||||
if (IsNPC() || current_level >= recommended_level) {
|
auto CalcItemBonus = [&](int statValue) -> int {
|
||||||
b->HP += item->HP;
|
return meets_recommended ? statValue : CalcRecommendedLevelBonus(current_level, recommended_level, statValue);
|
||||||
b->Mana += item->Mana;
|
};
|
||||||
b->Endurance += item->Endur;
|
|
||||||
|
|
||||||
b->AC += item->AC;
|
auto CalcCappedItemBonus = [&](int currentStat, int bonus, int cap) -> int {
|
||||||
b->ATK += item->Attack;
|
int calc_stat = currentStat + CalcItemBonus(bonus);
|
||||||
|
return IsOfClientBotMerc() ? std::min(cap, calc_stat) : calc_stat;
|
||||||
|
};
|
||||||
|
|
||||||
b->STR += (item->AStr + item->HeroicStr);
|
b->HP += CalcItemBonus(item->HP);
|
||||||
b->STA += (item->ASta + item->HeroicSta);
|
b->Mana += CalcItemBonus(item->Mana);
|
||||||
b->DEX += (item->ADex + item->HeroicDex);
|
b->Endurance += CalcItemBonus(item->Endur);
|
||||||
b->AGI += (item->AAgi + item->HeroicAgi);
|
b->AC += CalcItemBonus(item->AC);
|
||||||
b->INT += (item->AInt + item->HeroicInt);
|
|
||||||
b->WIS += (item->AWis + item->HeroicWis);
|
|
||||||
b->CHA += (item->ACha + item->HeroicCha);
|
|
||||||
|
|
||||||
b->MR += (item->MR + item->HeroicMR);
|
b->STR += CalcItemBonus(item->AStr + item->HeroicStr);
|
||||||
b->FR += (item->FR + item->HeroicFR);
|
b->STA += CalcItemBonus(item->ASta + item->HeroicSta);
|
||||||
b->CR += (item->CR + item->HeroicCR);
|
b->DEX += CalcItemBonus(item->ADex + item->HeroicDex);
|
||||||
b->PR += (item->PR + item->HeroicPR);
|
b->AGI += CalcItemBonus(item->AAgi + item->HeroicAgi);
|
||||||
b->DR += (item->DR + item->HeroicDR);
|
b->INT += CalcItemBonus(item->AInt + item->HeroicInt);
|
||||||
b->Corrup += (item->SVCorruption + item->HeroicSVCorrup);
|
b->WIS += CalcItemBonus(item->AWis + item->HeroicWis);
|
||||||
|
b->CHA += CalcItemBonus(item->ACha + item->HeroicCha);
|
||||||
|
|
||||||
|
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->STRCapMod += item->HeroicStr;
|
b->STRCapMod += item->HeroicStr;
|
||||||
b->STACapMod += item->HeroicSta;
|
b->STACapMod += item->HeroicSta;
|
||||||
@@ -315,251 +326,58 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
b->WISCapMod += item->HeroicWis;
|
b->WISCapMod += item->HeroicWis;
|
||||||
b->CHACapMod += item->HeroicCha;
|
b->CHACapMod += item->HeroicCha;
|
||||||
|
|
||||||
|
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 += item->HeroicMR;
|
b->MRCapMod += item->HeroicMR;
|
||||||
b->CRCapMod += item->HeroicFR;
|
b->FRCapMod += item->HeroicFR;
|
||||||
b->FRCapMod += item->HeroicCR;
|
b->CRCapMod += item->HeroicCR;
|
||||||
b->PRCapMod += item->HeroicPR;
|
b->PRCapMod += item->HeroicPR;
|
||||||
b->DRCapMod += item->HeroicDR;
|
b->DRCapMod += item->HeroicDR;
|
||||||
b->CorrupCapMod += item->HeroicSVCorrup;
|
b->CorrupCapMod += item->HeroicSVCorrup;
|
||||||
|
|
||||||
b->HeroicSTR += item->HeroicStr;
|
b->HPRegen += CalcItemBonus(item->Regen);
|
||||||
b->HeroicSTA += item->HeroicSta;
|
b->ManaRegen += CalcItemBonus(item->ManaRegen);
|
||||||
b->HeroicDEX += item->HeroicDex;
|
b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen);
|
||||||
b->HeroicAGI += item->HeroicAgi;
|
|
||||||
b->HeroicINT += item->HeroicInt;
|
|
||||||
b->HeroicWIS += item->HeroicWis;
|
|
||||||
b->HeroicCHA += item->HeroicCha;
|
|
||||||
|
|
||||||
b->HeroicMR += item->HeroicMR;
|
// These have rule-configured caps.
|
||||||
b->HeroicFR += item->HeroicFR;
|
b->ATK = CalcCappedItemBonus(b->ATK, item->Attack, RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap);
|
||||||
b->HeroicCR += item->HeroicCR;
|
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, RuleI(Character, ItemDamageShieldCap));
|
||||||
b->HeroicPR += item->HeroicPR;
|
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, RuleI(Character, ItemSpellShieldingCap));
|
||||||
b->HeroicDR += item->HeroicDR;
|
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, RuleI(Character, ItemShieldingCap));
|
||||||
b->HeroicCorrup += item->HeroicSVCorrup;
|
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, RuleI(Character, ItemStunResistCap));
|
||||||
} else {
|
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, RuleI(Character, ItemStrikethroughCap));
|
||||||
b->HP += CalcRecommendedLevelBonus(current_level, recommended_level, item->HP);
|
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, RuleI(Character, ItemAvoidanceCap));
|
||||||
b->Mana += CalcRecommendedLevelBonus(current_level, recommended_level, item->Mana);
|
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, RuleI(Character, ItemAccuracyCap));
|
||||||
b->Endurance += CalcRecommendedLevelBonus(current_level, recommended_level, item->Endur);
|
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, RuleI(Character, ItemCombatEffectsCap));
|
||||||
|
b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, RuleI(Character, ItemDoTShieldingCap));
|
||||||
b->AC += CalcRecommendedLevelBonus(current_level, recommended_level, item->AC);
|
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, RuleI(Character, ItemHealAmtCap));
|
||||||
b->ATK += CalcRecommendedLevelBonus(current_level, recommended_level, item->Attack);
|
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, RuleI(Character, ItemSpellDmgCap));
|
||||||
|
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, RuleI(Character, ItemClairvoyanceCap));
|
||||||
b->STR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AStr + item->HeroicStr));
|
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, RuleI(Character, ItemDSMitigationCap));
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b->haste < item->Haste) {
|
if (b->haste < item->Haste) {
|
||||||
b->haste = item->Haste;
|
b->haste = item->Haste;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Regen != 0) {
|
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
||||||
b->HPRegen += item->Regen;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->ManaRegen != 0) {
|
|
||||||
b->ManaRegen += item->ManaRegen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->EnduranceRegen != 0) {
|
|
||||||
b->EnduranceRegen += item->EnduranceRegen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Attack != 0) {
|
|
||||||
unsigned int cap = RuleI(Character, ItemATKCap);
|
|
||||||
cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap;
|
|
||||||
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->ATK + item->Attack) > cap
|
|
||||||
) {
|
|
||||||
b->ATK = RuleI(Character, ItemATKCap);
|
|
||||||
} else {
|
} else {
|
||||||
b->ATK += item->Attack;
|
b->SkillDamageAmount[item->ExtraDmgSkill] = CalcCappedItemBonus(b->SkillDamageAmount[item->ExtraDmgSkill], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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->SpellShield != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)
|
|
||||||
) {
|
|
||||||
b->SpellShield = RuleI(Character, ItemSpellShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->SpellShield += item->SpellShield;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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->StunResist != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)
|
|
||||||
) {
|
|
||||||
b->StunResist = RuleI(Character, ItemStunResistCap);
|
|
||||||
} else {
|
|
||||||
b->StunResist += item->StunResist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->StrikeThrough != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)
|
|
||||||
) {
|
|
||||||
b->StrikeThrough = RuleI(Character, ItemStrikethroughCap);
|
|
||||||
} else {
|
|
||||||
b->StrikeThrough += item->StrikeThrough;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,32 +496,6 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if (!is_augment) {
|
||||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||||
const auto* augment = inst->GetAugment(i);
|
const auto* augment = inst->GetAugment(i);
|
||||||
@@ -715,8 +507,6 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
+88
-118
@@ -2256,7 +2256,7 @@ void Bot::AI_Process()
|
|||||||
SetAttackingFlag(false);
|
SetAttackingFlag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
float tar_distance = DistanceSquared(m_Position, tar->GetPosition());
|
float tar_distance = DistanceSquaredNoZ(m_Position, tar->GetPosition());
|
||||||
|
|
||||||
// TARGET VALIDATION
|
// TARGET VALIDATION
|
||||||
if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) {
|
if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) {
|
||||||
@@ -2296,7 +2296,6 @@ void Bot::AI_Process()
|
|||||||
CombatRangeInput input = {
|
CombatRangeInput input = {
|
||||||
.target = tar,
|
.target = tar,
|
||||||
.target_distance = tar_distance,
|
.target_distance = tar_distance,
|
||||||
.behind_mob = behind_mob,
|
|
||||||
.stop_melee_level = stop_melee_level,
|
.stop_melee_level = stop_melee_level,
|
||||||
.p_item = p_item,
|
.p_item = p_item,
|
||||||
.s_item = s_item
|
.s_item = s_item
|
||||||
@@ -2368,6 +2367,16 @@ void Bot::AI_Process()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
HasTargetReflection() &&
|
||||||
|
!IsTaunting() &&
|
||||||
|
!tar->IsFleeing() &&
|
||||||
|
!tar->IsFeared() &&
|
||||||
|
TryEvade(tar)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// ENGAGED AT COMBAT RANGE
|
// ENGAGED AT COMBAT RANGE
|
||||||
|
|
||||||
// We can fight
|
// We can fight
|
||||||
@@ -2434,7 +2443,11 @@ void Bot::AI_Process()
|
|||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else if (!IsBotRanged() && GetLevel() < stop_melee_level) {
|
else if (!IsBotRanged() && GetLevel() < stop_melee_level) {
|
||||||
if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) {
|
if (
|
||||||
|
IsTaunting() ||
|
||||||
|
!GetMaxMeleeRange() ||
|
||||||
|
!RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)
|
||||||
|
) {
|
||||||
DoClassAttacks(tar);
|
DoClassAttacks(tar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3065,7 +3078,7 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
|||||||
|
|
||||||
// For races with a fixed size
|
// For races with a fixed size
|
||||||
if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) {
|
if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) {
|
||||||
// size_mod = 60.0f;
|
size_mod = 60.0f;
|
||||||
}
|
}
|
||||||
else if (size_mod < 6.0f) {
|
else if (size_mod < 6.0f) {
|
||||||
size_mod = 8.0f;
|
size_mod = 8.0f;
|
||||||
@@ -3110,92 +3123,40 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
|||||||
size_mod = (size_mod / 7.0f);
|
size_mod = (size_mod / 7.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
o.melee_distance_max = size_mod;
|
o.melee_distance_max = sqrt(size_mod);
|
||||||
|
|
||||||
if (!RuleB(Bots, UseFlatNormalMeleeRange)) {
|
|
||||||
|
|
||||||
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
|
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
|
||||||
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
|
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
|
||||||
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
||||||
|
|
||||||
switch (GetClass()) {
|
|
||||||
case Class::Warrior:
|
|
||||||
case Class::Paladin:
|
|
||||||
case Class::ShadowKnight:
|
|
||||||
o.melee_distance = (
|
|
||||||
is_two_hander ? o.melee_distance_max * 0.45f
|
|
||||||
: is_shield ? o.melee_distance_max * 0.35f
|
|
||||||
: o.melee_distance_max * 0.40f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Class::Necromancer:
|
|
||||||
case Class::Wizard:
|
|
||||||
case Class::Magician:
|
|
||||||
case Class::Enchanter:
|
|
||||||
o.melee_distance = (
|
|
||||||
is_two_hander ? o.melee_distance_max * 0.95f
|
|
||||||
: o.melee_distance_max * 0.75f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Class::Rogue:
|
|
||||||
o.melee_distance = (
|
|
||||||
input.behind_mob && is_backstab_weapon
|
|
||||||
? o.melee_distance_max * 0.35f
|
|
||||||
: o.melee_distance_max * 0.50f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
o.melee_distance = (
|
|
||||||
is_two_hander ? o.melee_distance_max * 0.70f
|
|
||||||
: o.melee_distance_max * 0.50f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
o.melee_distance = sqrt(o.melee_distance);
|
|
||||||
o.melee_distance_max = sqrt(o.melee_distance_max);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
o.melee_distance_max = sqrt(o.melee_distance_max);
|
|
||||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, NormalMeleeRangeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o.melee_distance > RuleR(Bots, MaxDistanceForMelee)) {
|
|
||||||
o.melee_distance = RuleR(Bots, MaxDistanceForMelee);
|
|
||||||
}
|
|
||||||
|
|
||||||
o.melee_distance_min = o.melee_distance * RuleR(Bots, PercentMinMeleeDistance);
|
|
||||||
|
|
||||||
if (IsTaunting()) {
|
|
||||||
o.melee_distance_min = o.melee_distance * RuleR(Bots, PercentTauntMinMeleeDistance);
|
|
||||||
o.melee_distance = o.melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_stop_melee_level = GetLevel() >= input.stop_melee_level;
|
bool is_stop_melee_level = GetLevel() >= input.stop_melee_level;
|
||||||
|
|
||||||
if (!IsTaunting() && !IsBotRanged() && !is_stop_melee_level && GetMaxMeleeRange()) {
|
if (IsTaunting()) { // Taunting bots
|
||||||
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance);
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerTauntingMeleeDistanceMultiplier);
|
||||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance);
|
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperTauntingMeleeDistanceMultiplier);
|
||||||
}
|
}
|
||||||
|
else if (IsBotRanged()) { // Archers/Throwers
|
||||||
if (is_stop_melee_level && !IsBotRanged()) {
|
|
||||||
float desired_range = GetBotDistanceRanged();
|
|
||||||
o.melee_distance_min = std::max(o.melee_distance, (desired_range / 2));
|
|
||||||
o.melee_distance = std::max((o.melee_distance + 1), desired_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsBotRanged()) {
|
|
||||||
float min_distance = RuleI(Combat, MinRangedAttackDist);
|
float min_distance = RuleI(Combat, MinRangedAttackDist);
|
||||||
float max_distance = GetBotRangedValue();
|
float max_distance = GetBotRangedValue();
|
||||||
float desired_range = GetBotDistanceRanged();
|
float desired_range = GetBotDistanceRanged();
|
||||||
max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged if set to ranged even if items/ammo aren't correct
|
|
||||||
|
max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged even if items/ammo aren't correct
|
||||||
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
||||||
o.melee_distance = std::min(max_distance, desired_range);
|
o.melee_distance = std::min(max_distance, desired_range);
|
||||||
}
|
}
|
||||||
|
else if (is_stop_melee_level) { // Casters
|
||||||
|
float desired_range = GetBotDistanceRanged();
|
||||||
|
|
||||||
|
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range / 2));
|
||||||
|
o.melee_distance = std::max((o.melee_distance_max * 1.25f), desired_range);
|
||||||
|
}
|
||||||
|
else if (GetMaxMeleeRange()) { // Melee bots set to max melee range
|
||||||
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerMaxMeleeRangeDistanceMultiplier);
|
||||||
|
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperMaxMeleeRangeDistanceMultiplier);
|
||||||
|
}
|
||||||
|
else { // Regular melee
|
||||||
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerMeleeDistanceMultiplier);
|
||||||
|
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperMeleeDistanceMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
o.at_combat_range = (input.target_distance <= o.melee_distance);
|
o.at_combat_range = (input.target_distance <= o.melee_distance);
|
||||||
|
|
||||||
@@ -3615,6 +3576,7 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) {
|
|||||||
void Bot::Depop() {
|
void Bot::Depop() {
|
||||||
WipeHateList();
|
WipeHateList();
|
||||||
entity_list.RemoveFromHateLists(this);
|
entity_list.RemoveFromHateLists(this);
|
||||||
|
RemoveAllAuras();
|
||||||
|
|
||||||
if (HasPet())
|
if (HasPet())
|
||||||
GetPet()->Depop();
|
GetPet()->Depop();
|
||||||
@@ -3728,7 +3690,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
|||||||
SetVerifiedRaid(true);
|
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
|
// Safety Check to confirm we have a valid group
|
||||||
auto owner = GetBotOwner();
|
auto owner = GetBotOwner();
|
||||||
if (owner && !group->IsGroupMember(owner->GetCleanName())) {
|
if (owner && !group->IsGroupMember(owner->GetCleanName())) {
|
||||||
@@ -3740,11 +3702,9 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MapSpellTypeLevels();
|
|
||||||
|
|
||||||
if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) {
|
if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) {
|
||||||
OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
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()) {
|
if (IsBotRanged()) {
|
||||||
@@ -9766,7 +9726,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
&&
|
&&
|
||||||
tar->CanBuffStack(spell_id, GetLevel(), true) < 0
|
tar->CanBuffStack(spell_id, GetLevel(), false) < 0
|
||||||
) {
|
) {
|
||||||
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
|
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
|
||||||
return false;
|
return false;
|
||||||
@@ -11847,21 +11807,19 @@ void Bot::DoCombatPositioning(
|
|||||||
bool front_mob
|
bool front_mob
|
||||||
) {
|
) {
|
||||||
if (HasTargetReflection()) {
|
if (HasTargetReflection()) {
|
||||||
if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) {
|
if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range
|
||||||
if (TryEvade(tar)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay
|
|
||||||
if (tar_distance <= melee_distance_max) {
|
if (tar_distance <= melee_distance_max) {
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), GetBehindMob(), false)) {
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 1.25f), GetBehindMob(), false)) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tar_distance < melee_distance_min || (!front_mob && IsTaunting())) { // Back up any bots that are too close
|
else if (
|
||||||
|
tar_distance < melee_distance_min ||
|
||||||
|
(!front_mob && IsTaunting())
|
||||||
|
) { // Back up any bots that are too close or if they're taunting and not in front of the mob
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
@@ -11871,46 +11829,58 @@ void Bot::DoCombatPositioning(
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!tar->IsFeared()) {
|
if (!tar->IsFeared()) {
|
||||||
if (IsTaunting()) { // Taunting adjustments
|
if (
|
||||||
Mob* mob_tar = tar->GetTarget();
|
tar_distance < melee_distance_min ||
|
||||||
|
(GetBehindMob() && !behind_mob) ||
|
||||||
if (!mob_tar) {
|
(IsTaunting() && !front_mob) ||
|
||||||
DoFaceCheckNoJitter(tar);
|
!HasRequiredLoSForPositioning(tar)
|
||||||
|
) { // Regular adjustment
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate
|
|
||||||
if (Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate)) {
|
|
||||||
Goal = mob_tar->GetPosition();
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // Otherwise, stick to any other bots that are taunting
|
|
||||||
if (mob_tar->IsBot() && mob_tar->CastToBot()->IsTaunting() && (Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))) {
|
|
||||||
Goal = mob_tar->GetPosition();
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tar_distance < melee_distance_min || (GetBehindMob() && !behind_mob) || (IsTaunting() && !front_mob) || !HasRequiredLoSForPositioning(tar)) { // Regular adjustment
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tar->IsEnraged() && !IsTaunting() && !stop_melee_level && !behind_mob) { // Move non-taunting melee bots behind target during enrage
|
else if (
|
||||||
|
tar->IsEnraged() &&
|
||||||
|
!IsTaunting() &&
|
||||||
|
!stop_melee_level &&
|
||||||
|
!behind_mob
|
||||||
|
) { // Move non-taunting melee bots behind target during enrage
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsTaunting()) { // Taunting adjustments
|
||||||
|
Mob* mob_tar = tar->GetTarget();
|
||||||
|
|
||||||
|
if (mob_tar) {
|
||||||
|
if (
|
||||||
|
RuleB(Bots, TauntingBotsFollowTopHate) &&
|
||||||
|
(Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))
|
||||||
|
) { // If enabled, taunting bots will stick to top hate
|
||||||
|
Goal = mob_tar->GetPosition();
|
||||||
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { // Otherwise, stick to any other bots that are taunting
|
||||||
|
if (
|
||||||
|
mob_tar->IsBot() &&
|
||||||
|
mob_tar->CastToBot()->IsTaunting() &&
|
||||||
|
(Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))
|
||||||
|
) {
|
||||||
|
Goal = mob_tar->GetPosition();
|
||||||
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-6
@@ -235,7 +235,6 @@ static std::map<uint16, std::string> botSubType_names = {
|
|||||||
struct CombatRangeInput {
|
struct CombatRangeInput {
|
||||||
Mob* target;
|
Mob* target;
|
||||||
float target_distance;
|
float target_distance;
|
||||||
bool behind_mob;
|
|
||||||
uint8 stop_melee_level;
|
uint8 stop_melee_level;
|
||||||
const EQ::ItemInstance* p_item;
|
const EQ::ItemInstance* p_item;
|
||||||
const EQ::ItemInstance* s_item;
|
const EQ::ItemInstance* s_item;
|
||||||
@@ -685,11 +684,9 @@ public:
|
|||||||
|
|
||||||
// Spell lists
|
// Spell lists
|
||||||
void CheckBotSpells();
|
void CheckBotSpells();
|
||||||
void MapSpellTypeLevels();
|
|
||||||
const std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; }
|
|
||||||
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
||||||
static uint16 GetParentSpellType(uint16 spell_type);
|
static uint16 GetParentSpellType(uint16 spell_type);
|
||||||
bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
||||||
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
||||||
void SetCastedSpellType(uint16 spell_type);
|
void SetCastedSpellType(uint16 spell_type);
|
||||||
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
||||||
@@ -1153,8 +1150,6 @@ protected:
|
|||||||
std::vector<BotSpells> AIBot_spells_enforced;
|
std::vector<BotSpells> AIBot_spells_enforced;
|
||||||
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
||||||
|
|
||||||
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spells_min_level;
|
|
||||||
|
|
||||||
std::vector<BotTimer> bot_timers;
|
std::vector<BotTimer> bot_timers;
|
||||||
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
#include "water_map.h"
|
#include "water_map.h"
|
||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
#include "mob.h"
|
#include "mob.h"
|
||||||
|
#include "bot_database.h"
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@@ -219,6 +220,8 @@ int bot_command_init(void)
|
|||||||
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
||||||
std::vector<std::string> orphaned_bot_command_settings;
|
std::vector<std::string> orphaned_bot_command_settings;
|
||||||
|
|
||||||
|
database.botdb.MapCommandedSpellTypeMinLevels();
|
||||||
|
|
||||||
for (auto bcs_iter : bot_command_settings) {
|
for (auto bcs_iter : bot_command_settings) {
|
||||||
|
|
||||||
auto bcl_iter = bot_command_list.find(bcs_iter.first);
|
auto bcl_iter = bot_command_list.find(bcs_iter.first);
|
||||||
@@ -796,10 +799,10 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& spell_map = bot->GetCommandedSpellTypesMinLevels();
|
auto spell_map = database.botdb.GetCommandedSpellTypesMinLevels();
|
||||||
|
|
||||||
if (spell_map.empty()) {
|
if (spell_map.empty()) {
|
||||||
bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type");
|
bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "../common/repositories/bot_pet_buffs_repository.h"
|
#include "../common/repositories/bot_pet_buffs_repository.h"
|
||||||
#include "../common/repositories/bot_pet_inventories_repository.h"
|
#include "../common/repositories/bot_pet_inventories_repository.h"
|
||||||
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
||||||
|
#include "../common/repositories/bot_spells_entries_repository.h"
|
||||||
#include "../common/repositories/bot_settings_repository.h"
|
#include "../common/repositories/bot_settings_repository.h"
|
||||||
#include "../common/repositories/bot_stances_repository.h"
|
#include "../common/repositories/bot_stances_repository.h"
|
||||||
#include "../common/repositories/bot_timers_repository.h"
|
#include "../common/repositories/bot_timers_repository.h"
|
||||||
@@ -2521,3 +2522,72 @@ bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BotDatabase::MapCommandedSpellTypeMinLevels() {
|
||||||
|
commanded_spell_type_min_levels.clear();
|
||||||
|
|
||||||
|
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||||
|
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||||
|
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (!Bot::IsValidBotSpellType(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = Class::Warrior; x <= Class::Berserker; ++x) {
|
||||||
|
commanded_spell_type_min_levels[i][x] = {UINT8_MAX, "" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
||||||
|
|
||||||
|
for (const auto& s : spell_list) {
|
||||||
|
if (!IsValidSpell(s.spell_id)) {
|
||||||
|
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell = spells[s.spell_id];
|
||||||
|
|
||||||
|
if (spell.target_type == ST_Self) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) ||
|
||||||
|
!Bot::IsValidBotSpellType(s.type)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (s.minlevel > commanded_spell_type_min_levels[i][bot_class].min_level) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
i > BotSpellTypes::PARENT_TYPE_END &&
|
||||||
|
i != s.type &&
|
||||||
|
Bot::GetParentSpellType(i) != s.type
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Bot::IsValidSpellTypeBySpellID(i, s.spell_id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.minlevel < commanded_spell_type_min_levels[i][bot_class].min_level) {
|
||||||
|
commanded_spell_type_min_levels[i][bot_class].min_level = s.minlevel;
|
||||||
|
commanded_spell_type_min_levels[i][bot_class].description = StringFormat(
|
||||||
|
"%s [#%u] - Level %u",
|
||||||
|
GetClassIDName(bot_class),
|
||||||
|
bot_class,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+8
-1
@@ -24,7 +24,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "bot_structs.h"
|
||||||
|
|
||||||
class Bot;
|
class Bot;
|
||||||
class Client;
|
class Client;
|
||||||
@@ -130,6 +130,9 @@ public:
|
|||||||
bool SaveBotSettings(Mob* m);
|
bool SaveBotSettings(Mob* m);
|
||||||
bool DeleteBotSettings(const uint32 bot_id);
|
bool DeleteBotSettings(const uint32 bot_id);
|
||||||
|
|
||||||
|
void MapCommandedSpellTypeMinLevels();
|
||||||
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; }
|
||||||
|
|
||||||
/* Bot group functions */
|
/* Bot group functions */
|
||||||
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
||||||
|
|
||||||
@@ -211,6 +214,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string query;
|
std::string query;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spell_type_min_levels;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2994,52 +2994,3 @@ void Bot::CheckBotSpells() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::MapSpellTypeLevels() {
|
|
||||||
commanded_spells_min_level.clear();
|
|
||||||
|
|
||||||
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
|
||||||
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
|
||||||
|
|
||||||
for (int i = start; i <= end; ++i) {
|
|
||||||
if (!Bot::IsValidBotSpellType(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int x = Class::Warrior; x <= Class::Berserker; ++x) {
|
|
||||||
commanded_spells_min_level[i][x] = { UINT8_MAX, "" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
|
||||||
|
|
||||||
for (const auto& s : spell_list) {
|
|
||||||
if (!IsValidSpell(s.spell_id)) {
|
|
||||||
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t spell_type = s.type;
|
|
||||||
int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX;
|
|
||||||
uint8_t min_level = s.minlevel;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) ||
|
|
||||||
!Bot::IsValidBotSpellType(spell_type)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& spell_info = commanded_spells_min_level[spell_type][bot_class];
|
|
||||||
|
|
||||||
if (min_level < spell_info.min_level) {
|
|
||||||
spell_info.min_level = min_level;
|
|
||||||
spell_info.description = StringFormat(
|
|
||||||
"%s [#%u]: Level %u",
|
|
||||||
GetClassIDName(bot_class),
|
|
||||||
bot_class,
|
|
||||||
min_level
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,259 @@
|
|||||||
|
#include "../../common/http/httplib.h"
|
||||||
|
#include "../../common/eqemu_logsys.h"
|
||||||
|
#include "../../common/platform.h"
|
||||||
|
#include "../zone.h"
|
||||||
|
#include "../client.h"
|
||||||
|
#include "../../common/net/eqstream.h"
|
||||||
|
|
||||||
|
extern Zone *zone;
|
||||||
|
|
||||||
|
void RunTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
} else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZoneCLI::DataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
|
{
|
||||||
|
if (cmd[{"-h", "--help"}]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 break_length = 50;
|
||||||
|
int failed_count = 0;
|
||||||
|
|
||||||
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
|
// boot shell zone for testing
|
||||||
|
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||||
|
zone->StopShutdownTimer();
|
||||||
|
|
||||||
|
entity_list.Process();
|
||||||
|
entity_list.MobProcess();
|
||||||
|
|
||||||
|
LogSys.EnableConsoleLogging();
|
||||||
|
|
||||||
|
std::cout << "===========================================\n";
|
||||||
|
std::cout << "⚙\uFE0F> Running DataBuckets Tests...\n";
|
||||||
|
std::cout << "===========================================\n\n";
|
||||||
|
|
||||||
|
Client *client = new Client();
|
||||||
|
|
||||||
|
// Basic Key-Value Set/Get
|
||||||
|
client->DeleteBucket("basic_key");
|
||||||
|
client->SetBucket("basic_key", "simple_value");
|
||||||
|
std::string value = client->GetBucket("basic_key");
|
||||||
|
RunTest("Basic Key-Value Set/Get", "simple_value", value);
|
||||||
|
|
||||||
|
// Overwriting a Key
|
||||||
|
client->SetBucket("basic_key", "new_value");
|
||||||
|
value = client->GetBucket("basic_key");
|
||||||
|
RunTest("Overwriting a Key", "new_value", value);
|
||||||
|
|
||||||
|
// Deleting a Key
|
||||||
|
client->DeleteBucket("basic_key");
|
||||||
|
value = client->GetBucket("basic_key");
|
||||||
|
RunTest("Deleting a Key", "", value);
|
||||||
|
|
||||||
|
// Setting a Key with an Expiration
|
||||||
|
client->SetBucket("expiring_key", "expires_soon", "S1");
|
||||||
|
value = client->GetBucket("expiring_key");
|
||||||
|
RunTest("Setting a Key with an Expiration", "expires_soon", value);
|
||||||
|
|
||||||
|
// Ensure Expired Key is Deleted
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
value = client->GetBucket("expiring_key");
|
||||||
|
RunTest("Ensure Expired Key is Deleted", "", value);
|
||||||
|
|
||||||
|
// Cache Read/Write Consistency
|
||||||
|
client->SetBucket("cache_key", "cached_value");
|
||||||
|
value = client->GetBucket("cache_key");
|
||||||
|
RunTest("Cache Read/Write Consistency", "cached_value", value);
|
||||||
|
|
||||||
|
// Cache Clears on Key Deletion
|
||||||
|
client->DeleteBucket("cache_key");
|
||||||
|
value = client->GetBucket("cache_key");
|
||||||
|
RunTest("Cache Clears on Key Deletion", "", value);
|
||||||
|
|
||||||
|
// Setting a Full JSON String
|
||||||
|
client->SetBucket("json_key", R"({"key1":"value1","key2":"value2"})");
|
||||||
|
value = client->GetBucket("json_key");
|
||||||
|
RunTest("Setting a Full JSON String", R"({"key1":"value1","key2":"value2"})", value);
|
||||||
|
|
||||||
|
// Overwriting JSON with a Simple String
|
||||||
|
client->SetBucket("json_key", "string_value");
|
||||||
|
value = client->GetBucket("json_key");
|
||||||
|
RunTest("Overwriting JSON with a Simple String", "string_value", value);
|
||||||
|
|
||||||
|
// Deleting Non-Existent Key
|
||||||
|
client->DeleteBucket("non_existent_key");
|
||||||
|
value = client->GetBucket("non_existent_key");
|
||||||
|
RunTest("Deleting Non-Existent Key", "", value);
|
||||||
|
|
||||||
|
// Basic Key-Value Storage**
|
||||||
|
client->DeleteBucket("simple_key"); // Reset
|
||||||
|
client->SetBucket("simple_key", "simple_value");
|
||||||
|
value = client->GetBucket("simple_key");
|
||||||
|
RunTest("Basic Key-Value Set/Get", "simple_value", value);
|
||||||
|
|
||||||
|
// Nested Key Storage**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
client->SetBucket("nested.test1", "value1");
|
||||||
|
client->SetBucket("nested.test2", "value2");
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Nested Key Set/Get", R"({"test1":"value1","test2":"value2"})", value);
|
||||||
|
|
||||||
|
// Prevent Overwriting Objects**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
client->SetBucket("nested.test1.a", "value1");
|
||||||
|
client->SetBucket("nested.test2.a", "value2");
|
||||||
|
client->SetBucket("nested.test2", "new_value"); // Should be **rejected**
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Prevent Overwriting Objects", R"({"test1":{"a":"value1"},"test2":{"a":"value2"}})", value);
|
||||||
|
|
||||||
|
// Deleting a Specific Nested Key**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
client->SetBucket("nested.test1", "value1");
|
||||||
|
client->SetBucket("nested.test2", "value2");
|
||||||
|
client->DeleteBucket("nested.test1");
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Delete Nested Key", R"({"test2":"value2"})", value);
|
||||||
|
|
||||||
|
// Deleting the Entire Parent Key**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Delete Parent Key", "", value);
|
||||||
|
|
||||||
|
// Expiration is Ignored for Nested Keys**
|
||||||
|
client->DeleteBucket("exp_test");
|
||||||
|
client->SetBucket("exp_test.nested", "data", "S20"); // Expiration ignored
|
||||||
|
value = client->GetBucket("exp_test");
|
||||||
|
RunTest("Expiration Ignored for Nested Keys", R"({"nested":"data"})", value);
|
||||||
|
|
||||||
|
// Cache Behavior**
|
||||||
|
client->DeleteBucket("cache_test");
|
||||||
|
client->SetBucket("cache_test", "cache_value");
|
||||||
|
value = client->GetBucket("cache_test");
|
||||||
|
RunTest("Cache Read/Write Consistency", "cache_value", value);
|
||||||
|
|
||||||
|
// Ensure Deleting Parent Key Clears Cache**
|
||||||
|
client->DeleteBucket("cache_test");
|
||||||
|
value = client->GetBucket("cache_test");
|
||||||
|
RunTest("Cache Clears on Parent Delete", "", value);
|
||||||
|
|
||||||
|
// Setting an Entire JSON Object**
|
||||||
|
client->DeleteBucket("full_json");
|
||||||
|
client->SetBucket("full_json", R"({"key1":"value1","key2":{"subkey":"subvalue"}})");
|
||||||
|
value = client->GetBucket("full_json");
|
||||||
|
RunTest("Set and Retrieve Full JSON Structure", R"({"key1":"value1","key2":{"subkey":"subvalue"}})", value);
|
||||||
|
|
||||||
|
// Partial Nested Key Deletion within JSON**
|
||||||
|
client->DeleteBucket("full_json");
|
||||||
|
client->SetBucket("full_json", R"({"key1":"value1","key2":{"subkey":"subvalue"}})");
|
||||||
|
client->DeleteBucket("full_json.key2");
|
||||||
|
value = client->GetBucket("full_json");
|
||||||
|
RunTest("Delete Nested Key within JSON", R"({"key1":"value1"})", value);
|
||||||
|
|
||||||
|
// Ensure Object Protection on Overwrite Attempt**
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
client->SetBucket("complex.nested", "overwrite_attempt"); // Should be rejected
|
||||||
|
value = client->GetBucket("complex");
|
||||||
|
RunTest("Ensure Object Protection on Overwrite Attempt", R"({"nested":{"obj1":"data1","obj2":"data2"}})", value);
|
||||||
|
|
||||||
|
// Deleting Non-Existent Key Doesn't Break Existing Data**
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
client->DeleteBucket("does_not_exist"); // Should do nothing
|
||||||
|
value = client->GetBucket("complex");
|
||||||
|
RunTest("Deleting Non-Existent Key Doesn't Break Existing Data", R"({"nested":{"obj1":"data1","obj2":"data2"}})", value);
|
||||||
|
|
||||||
|
// Get nested key value one level up **
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
value = client->GetBucket("complex.nested");
|
||||||
|
RunTest("Get nested key value", R"({"obj1":"data1","obj2":"data2"})", value);
|
||||||
|
|
||||||
|
// Get nested key value deep **
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
value = client->GetBucket("complex.nested.obj2");
|
||||||
|
RunTest("Get nested key value deep", R"(data2)", value);
|
||||||
|
|
||||||
|
// Retrieve Nested Key from Plain String**
|
||||||
|
client->DeleteBucket("plain_string");
|
||||||
|
client->SetBucket("plain_string", "some_value");
|
||||||
|
value = client->GetBucket("plain_string.nested");
|
||||||
|
RunTest("Retrieve Nested Key from Plain String", "", value);
|
||||||
|
|
||||||
|
// Store and Retrieve JSON Array**
|
||||||
|
client->DeleteBucket("json_array");
|
||||||
|
client->SetBucket("json_array", R"(["item1", "item2"])");
|
||||||
|
value = client->GetBucket("json_array");
|
||||||
|
RunTest("Store and Retrieve JSON Array", R"(["item1", "item2"])", value);
|
||||||
|
|
||||||
|
// // Prevent Overwriting Array with Object**
|
||||||
|
// client->DeleteBucket("json_array");
|
||||||
|
// client->SetBucket("json_array", R"(["item1", "item2"])");
|
||||||
|
// client->SetBucket("json_array.item", "new_value"); // Should be rejected
|
||||||
|
// value = client->GetBucket("json_array");
|
||||||
|
// RunTest("Prevent Overwriting Array with Object", R"(["item1", "item2"])", value);
|
||||||
|
|
||||||
|
// Retrieve Non-Existent Nested Key**
|
||||||
|
client->DeleteBucket("nested_partial");
|
||||||
|
client->SetBucket("nested_partial.level1", R"({"exists": "yes"})");
|
||||||
|
value = client->GetBucket("nested_partial.level1.non_existent");
|
||||||
|
RunTest("Retrieve Non-Existent Nested Key", "", value);
|
||||||
|
|
||||||
|
// Overwriting Parent Key Deletes Children**
|
||||||
|
client->DeleteBucket("nested_override");
|
||||||
|
client->SetBucket("nested_override.child", "data");
|
||||||
|
client->SetBucket("nested_override", "new_parent_value"); // Should remove `child`
|
||||||
|
value = client->GetBucket("nested_override");
|
||||||
|
RunTest("Overwriting Parent Key Deletes Children", "new_parent_value", value);
|
||||||
|
|
||||||
|
// Store and Retrieve Empty JSON Object**
|
||||||
|
client->DeleteBucket("empty_json");
|
||||||
|
client->SetBucket("empty_json", R"({})");
|
||||||
|
value = client->GetBucket("empty_json");
|
||||||
|
RunTest("Store and Retrieve Empty JSON Object", R"({})", value);
|
||||||
|
|
||||||
|
// Store and Retrieve JSON String**
|
||||||
|
client->DeleteBucket("json_string");
|
||||||
|
client->SetBucket("json_string", R"("this is a string")");
|
||||||
|
value = client->GetBucket("json_string");
|
||||||
|
RunTest("Store and Retrieve JSON String", R"("this is a string")", value);
|
||||||
|
|
||||||
|
// Deeply Nested Key Retrieval**
|
||||||
|
client->DeleteBucket("deep_nested");
|
||||||
|
client->SetBucket("deep_nested.level1.level2.level3.level4.level5", "final_value");
|
||||||
|
value = client->GetBucket("deep_nested.level1.level2.level3.level4.level5");
|
||||||
|
RunTest("Deeply Nested Key Retrieval", "final_value", value);
|
||||||
|
|
||||||
|
// Setting a Key with an Expiration
|
||||||
|
client->SetBucket("nested_expire.test.test", "shouldnt_expire", "S1");
|
||||||
|
value = client->GetBucket("nested_expire");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
RunTest("Setting a nested key with an expiration protection test", R"({"test":{"test":"shouldnt_expire"}})", value);
|
||||||
|
|
||||||
|
// Delete Deep Nested Key Keeps Parent**
|
||||||
|
// client->DeleteBucket("deep_nested");
|
||||||
|
// client->SetBucket("deep_nested.level1.level2.level3", R"({"key": "value"})");
|
||||||
|
// client->DeleteBucket("deep_nested.level1.level2.level3.key");
|
||||||
|
// value = client->GetBucket("deep_nested.level1.level2.level3");
|
||||||
|
// RunTest("Delete Deep Nested Key Keeps Parent", "{}", value);
|
||||||
|
|
||||||
|
std::cout << "\n===========================================\n";
|
||||||
|
std::cout << "✅ All DataBucket Tests Completed!\n";
|
||||||
|
std::cout << "===========================================\n";
|
||||||
|
}
|
||||||
+139
-108
@@ -4,50 +4,10 @@
|
|||||||
#include "../zone.h"
|
#include "../zone.h"
|
||||||
#include "../client.h"
|
#include "../client.h"
|
||||||
#include "../../common/net/eqstream.h"
|
#include "../../common/net/eqstream.h"
|
||||||
|
#include "../../common/json/json.hpp"
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
|
using json = nlohmann::json;
|
||||||
void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
|
||||||
{
|
|
||||||
if (cmd[{"-h", "--help"}]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 break_length = 50;
|
|
||||||
int failed_count = 0;
|
|
||||||
|
|
||||||
RegisterExecutablePlatform(EQEmuExePlatform::ExePlatformZoneSidecar);
|
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
LogInfo("Booting test zone for NPC handins");
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
|
|
||||||
LogSys.SilenceConsoleLogging();
|
|
||||||
|
|
||||||
Zone::Bootup(ZoneID("qrg"), 0, false);
|
|
||||||
zone->StopShutdownTimer();
|
|
||||||
|
|
||||||
entity_list.Process();
|
|
||||||
entity_list.MobProcess();
|
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
LogInfo("> Done booting test zone");
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
|
|
||||||
Client *c = new Client();
|
|
||||||
auto npc_type = content_db.LoadNPCTypesData(754008);
|
|
||||||
if (npc_type) {
|
|
||||||
auto npc = new NPC(
|
|
||||||
npc_type,
|
|
||||||
nullptr,
|
|
||||||
glm::vec4(0, 0, 0, 0),
|
|
||||||
GravityBehavior::Water
|
|
||||||
);
|
|
||||||
|
|
||||||
entity_list.AddNPC(npc);
|
|
||||||
|
|
||||||
LogInfo("> Spawned NPC [{}]", npc->GetCleanName());
|
|
||||||
LogInfo("> Spawned client [{}]", c->GetCleanName());
|
|
||||||
|
|
||||||
struct HandinEntry {
|
struct HandinEntry {
|
||||||
std::string item_id = "0";
|
std::string item_id = "0";
|
||||||
@@ -69,13 +29,82 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct TestCase {
|
struct TestCase {
|
||||||
std::string description = "";
|
std::string description;
|
||||||
Handin hand_in;
|
Handin hand_in;
|
||||||
Handin required;
|
Handin required;
|
||||||
Handin returned;
|
Handin returned;
|
||||||
bool handin_check_result;
|
bool handin_check_result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void RunTest(const std::string &test_name, bool expected, bool actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << (expected ? "true" : "false") << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << (actual ? "true" : "false") << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunSerializedTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SerializeHandin(const std::map<std::string, uint32> &items, const HandinMoney &money)
|
||||||
|
{
|
||||||
|
json j;
|
||||||
|
j["items"] = items;
|
||||||
|
j["money"] = {
|
||||||
|
{"platinum", money.platinum},
|
||||||
|
{"gold", money.gold},
|
||||||
|
{"silver", money.silver},
|
||||||
|
{"copper", money.copper}
|
||||||
|
};
|
||||||
|
return j.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
|
{
|
||||||
|
if (cmd[{"-h", "--help"}]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
|
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||||
|
zone->StopShutdownTimer();
|
||||||
|
|
||||||
|
entity_list.Process();
|
||||||
|
entity_list.MobProcess();
|
||||||
|
|
||||||
|
std::cout << "===========================================\n";
|
||||||
|
std::cout << "⚙\uFE0F> Running Hand-in Tests...\n";
|
||||||
|
std::cout << "===========================================\n\n";
|
||||||
|
|
||||||
|
Client *c = new Client();
|
||||||
|
auto npc_type = content_db.LoadNPCTypesData(754008);
|
||||||
|
if (npc_type) {
|
||||||
|
auto npc = new NPC(
|
||||||
|
npc_type,
|
||||||
|
nullptr,
|
||||||
|
glm::vec4(0, 0, 0, 0),
|
||||||
|
GravityBehavior::Water
|
||||||
|
);
|
||||||
|
|
||||||
|
entity_list.AddNPC(npc);
|
||||||
|
|
||||||
std::vector<TestCase> test_cases = {
|
std::vector<TestCase> test_cases = {
|
||||||
TestCase{
|
TestCase{
|
||||||
.description = "Test basic cloth-cap hand-in",
|
.description = "Test basic cloth-cap hand-in",
|
||||||
@@ -155,7 +184,10 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
.items = {},
|
.items = {},
|
||||||
.money = {.platinum = 100},
|
.money = {.platinum = 100},
|
||||||
},
|
},
|
||||||
.returned = {},
|
.returned = {
|
||||||
|
.items = {},
|
||||||
|
.money = {.platinum = 1},
|
||||||
|
},
|
||||||
.handin_check_result = false,
|
.handin_check_result = false,
|
||||||
},
|
},
|
||||||
TestCase{
|
TestCase{
|
||||||
@@ -168,7 +200,10 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
.items = {},
|
.items = {},
|
||||||
.money = {.platinum = 100, .gold = 100, .silver = 100, .copper = 100},
|
.money = {.platinum = 100, .gold = 100, .silver = 100, .copper = 100},
|
||||||
},
|
},
|
||||||
.returned = {},
|
.returned = {
|
||||||
|
.items = {},
|
||||||
|
.money = {.platinum = 1, .gold = 1, .silver = 1, .copper = 1},
|
||||||
|
},
|
||||||
.handin_check_result = false,
|
.handin_check_result = false,
|
||||||
},
|
},
|
||||||
TestCase{
|
TestCase{
|
||||||
@@ -217,9 +252,12 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
},
|
},
|
||||||
.returned = {
|
.returned = {
|
||||||
.items = {
|
.items = {
|
||||||
HandinEntry{.item_id = "1001", .count = 1},
|
HandinEntry{
|
||||||
|
.item_id = "1001", .count = 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.money = {.platinum = 1},
|
||||||
|
},
|
||||||
.handin_check_result = false,
|
.handin_check_result = false,
|
||||||
},
|
},
|
||||||
TestCase{
|
TestCase{
|
||||||
@@ -304,12 +342,7 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
HandinEntry{.item_id = "1007", .count = 1},
|
HandinEntry{.item_id = "1007", .count = 1},
|
||||||
HandinEntry{.item_id = "1007", .count = 1},
|
HandinEntry{.item_id = "1007", .count = 1},
|
||||||
},
|
},
|
||||||
.money = {
|
.money = {},
|
||||||
.platinum = 1,
|
|
||||||
.gold = 666,
|
|
||||||
.silver = 234,
|
|
||||||
.copper = 444,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
.handin_check_result = true,
|
.handin_check_result = true,
|
||||||
},
|
},
|
||||||
@@ -411,14 +444,12 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
// turn this on to see debugging output
|
// turn this on to see debugging output
|
||||||
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
for (auto &test: test_cases) {
|
||||||
|
|
||||||
for (auto &test_case: test_cases) {
|
|
||||||
hand_ins.clear();
|
hand_ins.clear();
|
||||||
required.clear();
|
required.clear();
|
||||||
items.clear();
|
items.clear();
|
||||||
|
|
||||||
for (auto &hand_in: test_case.hand_in.items) {
|
for (auto &hand_in: test.hand_in.items) {
|
||||||
auto item_id = Strings::ToInt(hand_in.item_id);
|
auto item_id = Strings::ToInt(hand_in.item_id);
|
||||||
EQ::ItemInstance *inst = database.CreateItem(item_id);
|
EQ::ItemInstance *inst = database.CreateItem(item_id);
|
||||||
if (inst->IsStackable()) {
|
if (inst->IsStackable()) {
|
||||||
@@ -434,72 +465,76 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.hand_in.money.platinum > 0) {
|
if (test.hand_in.money.platinum > 0) {
|
||||||
hand_ins["platinum"] = test_case.hand_in.money.platinum;
|
hand_ins["platinum"] = test.hand_in.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.gold > 0) {
|
if (test.hand_in.money.gold > 0) {
|
||||||
hand_ins["gold"] = test_case.hand_in.money.gold;
|
hand_ins["gold"] = test.hand_in.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.silver > 0) {
|
if (test.hand_in.money.silver > 0) {
|
||||||
hand_ins["silver"] = test_case.hand_in.money.silver;
|
hand_ins["silver"] = test.hand_in.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.copper > 0) {
|
if (test.hand_in.money.copper > 0) {
|
||||||
hand_ins["copper"] = test_case.hand_in.money.copper;
|
hand_ins["copper"] = test.hand_in.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &req: test_case.required.items) {
|
for (auto &req: test.required.items) {
|
||||||
required[req.item_id] = req.count;
|
required[req.item_id] = req.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.required.money.platinum > 0) {
|
if (test.required.money.platinum > 0) {
|
||||||
required["platinum"] = test_case.required.money.platinum;
|
required["platinum"] = test.required.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.gold > 0) {
|
if (test.required.money.gold > 0) {
|
||||||
required["gold"] = test_case.required.money.gold;
|
required["gold"] = test.required.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.silver > 0) {
|
if (test.required.money.silver > 0) {
|
||||||
required["silver"] = test_case.required.money.silver;
|
required["silver"] = test.required.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.copper > 0) {
|
if (test.required.money.copper > 0) {
|
||||||
required["copper"] = test_case.required.money.copper;
|
required["copper"] = test.required.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
||||||
if (result != test_case.handin_check_result) {
|
|
||||||
failed_count++;
|
RunTest(test.description, test.handin_check_result, result);
|
||||||
LogError("FAIL [{}]", test_case.description);
|
|
||||||
// print out the hand-ins
|
|
||||||
LogError("Hand-ins >");
|
|
||||||
for (auto &hand_in: hand_ins) {
|
|
||||||
LogError(" > Item [{}] count [{}]", hand_in.first, hand_in.second);
|
|
||||||
}
|
|
||||||
LogError("Required >");
|
|
||||||
for (auto &req: required) {
|
|
||||||
LogError(" > Item [{}] count [{}]", req.first, req.second);
|
|
||||||
}
|
|
||||||
LogError("Expected [{}] got [{}]", test_case.handin_check_result, result);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("PASS [{}]", test_case.description);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto returned = npc->ReturnHandinItems(c);
|
auto returned = npc->ReturnHandinItems(c);
|
||||||
|
|
||||||
// assert that returned items are expected
|
std::map<std::string, uint32> returned_items;
|
||||||
for (auto &item: test_case.returned.items) {
|
HandinMoney returned_money{};
|
||||||
auto found = false;
|
|
||||||
for (auto &ret: returned.items) {
|
// Serialize returned items
|
||||||
if (ret.item_id == item.item_id) {
|
for (const auto &ret: returned.items) {
|
||||||
found = true;
|
// if (ret.item->IsStackable() && ret.item->GetCharges() != ret.count) {
|
||||||
break;
|
// ret.item->SetCharges(ret.count);
|
||||||
}
|
// }
|
||||||
}
|
returned_items[ret.item_id] += ret.count;
|
||||||
if (!found) {
|
|
||||||
LogError("Returned item [{}] not expected", item.item_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serialize returned money
|
||||||
|
returned_money.platinum = returned.money.platinum;
|
||||||
|
returned_money.gold = returned.money.gold;
|
||||||
|
returned_money.silver = returned.money.silver;
|
||||||
|
returned_money.copper = returned.money.copper;
|
||||||
|
|
||||||
|
// Serialize expected and actual return values for comparison
|
||||||
|
std::map<std::string, uint32> expected_returned_items;
|
||||||
|
for (const auto &entry: test.returned.items) {
|
||||||
|
expected_returned_items[entry.item_id] += entry.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string expected_serialized = SerializeHandin(
|
||||||
|
expected_returned_items,
|
||||||
|
test.returned.money
|
||||||
|
);
|
||||||
|
|
||||||
|
std::string actual_serialized = SerializeHandin(returned_items, returned_money);
|
||||||
|
|
||||||
|
// Run serialization check test
|
||||||
|
RunSerializedTest(test.description + " (Return Validation)", expected_serialized, actual_serialized);
|
||||||
|
|
||||||
npc->ResetHandin();
|
npc->ResetHandin();
|
||||||
|
|
||||||
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
||||||
@@ -508,11 +543,7 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed_count > 0) {
|
std::cout << "\n===========================================\n";
|
||||||
LogError("Failed [{}] tests", failed_count);
|
std::cout << "✅ All NPC Hand-in Tests Completed!\n";
|
||||||
std::exit(1);
|
std::cout << "===========================================\n";
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("All tests passed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,6 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
uint32 break_length = 50;
|
uint32 break_length = 50;
|
||||||
int failed_count = 0;
|
int failed_count = 0;
|
||||||
|
|
||||||
RegisterExecutablePlatform(EQEmuExePlatform::ExePlatformZoneSidecar);
|
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
LogInfo("Booting test zone for NPC handins (MultiQuest)");
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
|
|
||||||
LogSys.SilenceConsoleLogging();
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
Zone::Bootup(ZoneID("qrg"), 0, false);
|
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||||
@@ -30,9 +24,9 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
entity_list.Process();
|
entity_list.Process();
|
||||||
entity_list.MobProcess();
|
entity_list.MobProcess();
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
std::cout << "===========================================\n";
|
||||||
LogInfo("> Done booting test zone");
|
std::cout << "⚙\uFE0F> Running Hand-in Tests (Multi-Quest)...\n";
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
std::cout << "===========================================\n\n";
|
||||||
|
|
||||||
Client *c = new Client();
|
Client *c = new Client();
|
||||||
auto npc_type = content_db.LoadNPCTypesData(754008);
|
auto npc_type = content_db.LoadNPCTypesData(754008);
|
||||||
@@ -47,9 +41,6 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
entity_list.AddNPC(npc);
|
entity_list.AddNPC(npc);
|
||||||
npc->MultiQuestEnable();
|
npc->MultiQuestEnable();
|
||||||
|
|
||||||
LogInfo("> Spawned NPC [{}]", npc->GetCleanName());
|
|
||||||
LogInfo("> Spawned client [{}]", c->GetCleanName());
|
|
||||||
|
|
||||||
struct HandinEntry {
|
struct HandinEntry {
|
||||||
std::string item_id = "0";
|
std::string item_id = "0";
|
||||||
uint32 count = 0;
|
uint32 count = 0;
|
||||||
@@ -108,12 +99,10 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
// turn this on to see debugging output
|
// turn this on to see debugging output
|
||||||
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
for (auto &test: test_cases) {
|
||||||
|
|
||||||
for (auto &test_case: test_cases) {
|
|
||||||
required.clear();
|
required.clear();
|
||||||
|
|
||||||
for (auto &hand_in: test_case.hand_in.items) {
|
for (auto &hand_in: test.hand_in.items) {
|
||||||
hand_ins.clear();
|
hand_ins.clear();
|
||||||
items.clear();
|
items.clear();
|
||||||
|
|
||||||
@@ -135,72 +124,43 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.hand_in.money.platinum > 0) {
|
if (test.hand_in.money.platinum > 0) {
|
||||||
hand_ins["platinum"] = test_case.hand_in.money.platinum;
|
hand_ins["platinum"] = test.hand_in.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.gold > 0) {
|
if (test.hand_in.money.gold > 0) {
|
||||||
hand_ins["gold"] = test_case.hand_in.money.gold;
|
hand_ins["gold"] = test.hand_in.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.silver > 0) {
|
if (test.hand_in.money.silver > 0) {
|
||||||
hand_ins["silver"] = test_case.hand_in.money.silver;
|
hand_ins["silver"] = test.hand_in.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.copper > 0) {
|
if (test.hand_in.money.copper > 0) {
|
||||||
hand_ins["copper"] = test_case.hand_in.money.copper;
|
hand_ins["copper"] = test.hand_in.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &req: test_case.required.items) {
|
for (auto &req: test.required.items) {
|
||||||
required[req.item_id] = req.count;
|
required[req.item_id] = req.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.required.money.platinum > 0) {
|
if (test.required.money.platinum > 0) {
|
||||||
required["platinum"] = test_case.required.money.platinum;
|
required["platinum"] = test.required.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.gold > 0) {
|
if (test.required.money.gold > 0) {
|
||||||
required["gold"] = test_case.required.money.gold;
|
required["gold"] = test.required.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.silver > 0) {
|
if (test.required.money.silver > 0) {
|
||||||
required["silver"] = test_case.required.money.silver;
|
required["silver"] = test.required.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.copper > 0) {
|
if (test.required.money.copper > 0) {
|
||||||
required["copper"] = test_case.required.money.copper;
|
required["copper"] = test.required.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
||||||
if (result != test_case.handin_check_result) {
|
|
||||||
failed_count++;
|
RunTest(test.description, test.handin_check_result, result);
|
||||||
LogError("FAIL [{}]", test_case.description);
|
|
||||||
// print out the hand-ins
|
|
||||||
LogError("Hand-ins >");
|
|
||||||
for (auto &item: npc->GetHandin().items) {
|
|
||||||
LogError(" > Item [{}] count [{}]", item.item_id, item.count);
|
|
||||||
}
|
|
||||||
LogError("Required >");
|
|
||||||
for (auto &req: required) {
|
|
||||||
LogError(" > Item [{}] count [{}]", req.first, req.second);
|
|
||||||
}
|
|
||||||
LogError("Expected [{}] got [{}]", test_case.handin_check_result, result);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("PASS [{}]", test_case.description);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto returned = npc->ReturnHandinItems(c);
|
auto returned = npc->ReturnHandinItems(c);
|
||||||
|
|
||||||
// assert that returned items are expected
|
|
||||||
for (auto &item: test_case.returned.items) {
|
|
||||||
auto found = false;
|
|
||||||
for (auto &ret: returned.items) {
|
|
||||||
if (ret.item_id == item.item_id) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
LogError("Returned item [{}] not expected", item.item_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
npc->ResetHandin();
|
npc->ResetHandin();
|
||||||
|
|
||||||
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
||||||
@@ -209,11 +169,7 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed_count > 0) {
|
std::cout << "\n===========================================\n";
|
||||||
LogError("Failed [{}] tests", failed_count);
|
std::cout << "✅ All NPC Hand-in Tests Completed (Multi-Quest)!\n";
|
||||||
std::exit(1);
|
std::cout << "===========================================\n";
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("All tests passed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-19
@@ -708,6 +708,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client() {
|
Client::~Client() {
|
||||||
|
entity_list.RemoveMobFromCloseLists(this);
|
||||||
|
m_close_mobs.clear();
|
||||||
|
|
||||||
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB (Parcel, EnableParcelMerchants)) {
|
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB (Parcel, EnableParcelMerchants)) {
|
||||||
DoParcelCancel();
|
DoParcelCancel();
|
||||||
}
|
}
|
||||||
@@ -965,6 +968,10 @@ bool Client::SaveAA()
|
|||||||
|
|
||||||
m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa;
|
m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa;
|
||||||
|
|
||||||
|
if (v.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v);
|
return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4622,7 +4629,7 @@ void Client::KeyRingLoad()
|
|||||||
const auto &l = KeyringRepository::GetWhere(
|
const auto &l = KeyringRepository::GetWhere(
|
||||||
database,
|
database,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"`char_id` = {} ORDER BY `item_id`",
|
"`char_id` = {} ORDER BY `item_id` ASC",
|
||||||
character_id
|
character_id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -4631,21 +4638,15 @@ void Client::KeyRingLoad()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (const auto& e : l) {
|
for (const auto& e : l) {
|
||||||
keyring.emplace_back(e.item_id);
|
keyring.emplace_back(e.item_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::KeyRingAdd(uint32 item_id)
|
bool Client::KeyRingAdd(uint32 item_id)
|
||||||
{
|
{
|
||||||
if (!item_id) {
|
if (!item_id || KeyRingCheck(item_id)) {
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
const bool found = KeyRingCheck(item_id);
|
|
||||||
if (found) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto e = KeyringRepository::NewEntity();
|
auto e = KeyringRepository::NewEntity();
|
||||||
@@ -4656,14 +4657,14 @@ void Client::KeyRingAdd(uint32 item_id)
|
|||||||
e = KeyringRepository::InsertOne(database, e);
|
e = KeyringRepository::InsertOne(database, e);
|
||||||
|
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyring.emplace_back(item_id);
|
keyring.emplace_back(item_id);
|
||||||
|
|
||||||
if (!RuleB(World, UseItemLinksForKeyRing)) {
|
if (!RuleB(World, UseItemLinksForKeyRing)) {
|
||||||
Message(Chat::LightBlue, "Added to keyring.");
|
Message(Chat::LightBlue, "Added to keyring.");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &item_link = database.CreateItemLink(item_id);
|
const std::string &item_link = database.CreateItemLink(item_id);
|
||||||
@@ -4675,17 +4676,25 @@ void Client::KeyRingAdd(uint32 item_id)
|
|||||||
item_link
|
item_link
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::KeyRingCheck(uint32 item_id)
|
bool Client::KeyRingCheck(uint32 item_id)
|
||||||
{
|
{
|
||||||
for (const auto &e : keyring) {
|
return std::find(keyring.begin(), keyring.end(), item_id) != keyring.end();
|
||||||
if (e == item_id) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
bool Client::KeyRingClear()
|
||||||
|
{
|
||||||
|
keyring.clear();
|
||||||
|
|
||||||
|
return KeyringRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`char_id` = {}",
|
||||||
|
CharacterID()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::KeyRingList()
|
void Client::KeyRingList()
|
||||||
@@ -4694,16 +4703,40 @@ void Client::KeyRingList()
|
|||||||
|
|
||||||
const EQ::ItemData *item = nullptr;
|
const EQ::ItemData *item = nullptr;
|
||||||
|
|
||||||
for (const auto &e : keyring) {
|
for (const uint32& e : keyring) {
|
||||||
item = database.GetItem(e);
|
item = database.GetItem(e);
|
||||||
if (item) {
|
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());
|
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() {
|
bool Client::IsPetNameChangeAllowed() {
|
||||||
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+35
-4
@@ -327,8 +327,10 @@ public:
|
|||||||
// void TraderPriceUpdate(const EQApplicationPacket *app);
|
// void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||||
uint8 WithCustomer(uint16 NewCustomer);
|
uint8 WithCustomer(uint16 NewCustomer);
|
||||||
void KeyRingLoad();
|
void KeyRingLoad();
|
||||||
void KeyRingAdd(uint32 item_id);
|
bool KeyRingAdd(uint32 item_id);
|
||||||
bool KeyRingCheck(uint32 item_id);
|
bool KeyRingCheck(uint32 item_id);
|
||||||
|
bool KeyRingClear();
|
||||||
|
bool KeyRingRemove(uint32 item_id);
|
||||||
void KeyRingList();
|
void KeyRingList();
|
||||||
bool IsPetNameChangeAllowed();
|
bool IsPetNameChangeAllowed();
|
||||||
void GrantPetNameChange();
|
void GrantPetNameChange();
|
||||||
@@ -402,6 +404,7 @@ public:
|
|||||||
void LoadParcels();
|
void LoadParcels();
|
||||||
std::map<uint32, CharacterParcelsRepository::CharacterParcels> GetParcels() { return m_parcels; }
|
std::map<uint32, CharacterParcelsRepository::CharacterParcels> GetParcels() { return m_parcels; }
|
||||||
int32 FindNextFreeParcelSlot(uint32 char_id);
|
int32 FindNextFreeParcelSlot(uint32 char_id);
|
||||||
|
int32 FindNextFreeParcelSlotUsingMemory();
|
||||||
void SendParcelIconStatus();
|
void SendParcelIconStatus();
|
||||||
|
|
||||||
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
||||||
@@ -1289,6 +1292,29 @@ public:
|
|||||||
void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false);
|
void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false);
|
||||||
|
|
||||||
// Task System Methods
|
// 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 LoadClientTaskState();
|
||||||
void RemoveClientTaskState();
|
void RemoveClientTaskState();
|
||||||
void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1);
|
void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1);
|
||||||
@@ -1459,7 +1485,10 @@ public:
|
|||||||
{
|
{
|
||||||
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
|
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)
|
inline bool AreTasksCompleted(std::vector<int> task_ids)
|
||||||
{
|
{
|
||||||
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
|
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
|
||||||
@@ -1872,7 +1901,7 @@ public:
|
|||||||
void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item);
|
void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item);
|
||||||
void DoEvolveItemToggle(const EQApplicationPacket* app);
|
void DoEvolveItemToggle(const EQApplicationPacket* app);
|
||||||
void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app);
|
void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app);
|
||||||
bool DoEvolveCheckProgression(const EQ::ItemInstance &inst);
|
bool DoEvolveCheckProgression(EQ::ItemInstance &inst);
|
||||||
void SendEvolveXPWindowDetails(const EQApplicationPacket* app);
|
void SendEvolveXPWindowDetails(const EQApplicationPacket* app);
|
||||||
void DoEvolveTransferXP(const EQApplicationPacket* app);
|
void DoEvolveTransferXP(const EQApplicationPacket* app);
|
||||||
void SendEvolveXPTransferWindow();
|
void SendEvolveXPTransferWindow();
|
||||||
@@ -2012,7 +2041,7 @@ private:
|
|||||||
bool GuildBanker;
|
bool GuildBanker;
|
||||||
uint16 duel_target;
|
uint16 duel_target;
|
||||||
bool duelaccepted;
|
bool duelaccepted;
|
||||||
std::list<uint32> keyring;
|
std::vector<uint32> keyring;
|
||||||
bool tellsoff; // GM /toggle
|
bool tellsoff; // GM /toggle
|
||||||
bool gm_hide_me;
|
bool gm_hide_me;
|
||||||
bool LFG;
|
bool LFG;
|
||||||
@@ -2290,6 +2319,8 @@ private:
|
|||||||
bool m_has_quest_compass = false;
|
bool m_has_quest_compass = false;
|
||||||
std::vector<uint32_t> m_dynamic_zone_ids;
|
std::vector<uint32_t> m_dynamic_zone_ids;
|
||||||
|
|
||||||
|
std::vector<uint32_t> m_completed_shared_tasks;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum BotOwnerOption : size_t {
|
enum BotOwnerOption : size_t {
|
||||||
booDeathMarquee,
|
booDeathMarquee,
|
||||||
|
|||||||
+119
-25
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
extern WorldServer worldserver;
|
extern WorldServer worldserver;
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
|
const std::string SUB_TYPE_DELIMITER = ".";
|
||||||
|
|
||||||
void Client::DoEvolveItemToggle(const EQApplicationPacket *app)
|
void Client::DoEvolveItemToggle(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
@@ -70,18 +71,19 @@ void Client::SendEvolvingPacket(const int8 action, const CharacterEvolvingItemsR
|
|||||||
|
|
||||||
void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||||
{
|
{
|
||||||
std::vector<const EQ::ItemInstance *> queue{};
|
std::vector<EQ::ItemInstance *> queue{};
|
||||||
|
|
||||||
for (auto &[key, inst]: GetInv().GetWorn()) {
|
for (auto &[key, inst]: GetInv().GetWorn()) {
|
||||||
LogEvolveItemDetail(
|
LogEvolveItemDetail(
|
||||||
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
|
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
|
||||||
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
|
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
|
||||||
LogEvolveItemDetail(
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) {
|
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check(false)) {
|
||||||
LogEvolveItemDetail(
|
LogEvolveItemDetail(
|
||||||
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] secs remaining.",
|
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] secs remaining.",
|
||||||
CharacterID(),
|
CharacterID(),
|
||||||
@@ -98,32 +100,40 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
CharacterID(),
|
CharacterID(),
|
||||||
inst->GetID(),
|
inst->GetID(),
|
||||||
type,
|
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) {
|
switch (type) {
|
||||||
case EvolvingItems::Types::AMOUNT_OF_EXP: {
|
case EvolvingItems::Types::AMOUNT_OF_EXP: {
|
||||||
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
|
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
|
||||||
if (sub_type == EvolvingItems::SubTypes::ALL_EXP ||
|
|
||||||
(sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) {
|
// Determine the evolve amount based on sub_type conditions
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
int evolve_amount = 0;
|
||||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100);
|
|
||||||
|
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 (
|
else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||||
sub_type == EvolvingItems::SubTypes::ALL_EXP ||
|
(has_sub_type(EvolvingItems::SubTypes::RAID_EXP) && IsRaidGrouped())) {
|
||||||
(sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) {
|
evolve_amount = exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100;
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
|
||||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100);
|
|
||||||
}
|
}
|
||||||
else if (
|
else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||||
sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) {
|
has_sub_type(EvolvingItems::SubTypes::SOLO_EXP)) {
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
evolve_amount = exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100;
|
||||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inst->SetEvolveAddToCurrentAmount(evolve_amount);
|
||||||
inst->CalculateEvolveProgression();
|
inst->CalculateEvolveProgression();
|
||||||
|
|
||||||
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
||||||
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression());
|
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -146,7 +156,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
}
|
}
|
||||||
case EvolvingItems::Types::SPECIFIC_MOB_RACE: {
|
case EvolvingItems::Types::SPECIFIC_MOB_RACE: {
|
||||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
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);
|
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||||
inst->SetEvolveAddToCurrentAmount(1);
|
inst->SetEvolveAddToCurrentAmount(1);
|
||||||
inst->CalculateEvolveProgression();
|
inst->CalculateEvolveProgression();
|
||||||
@@ -155,7 +165,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
database,
|
database,
|
||||||
inst->GetEvolveUniqueID(),
|
inst->GetEvolveUniqueID(),
|
||||||
inst->GetEvolveCurrentAmount(),
|
inst->GetEvolveCurrentAmount(),
|
||||||
inst->GetEvolveProgression());
|
inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -163,7 +174,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||||
|
|
||||||
LogEvolveItem(
|
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}]",
|
"- Increased count by 1 for <green>[{1}]",
|
||||||
sub_type,
|
sub_type,
|
||||||
inst->GetID()
|
inst->GetID()
|
||||||
@@ -178,7 +190,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
}
|
}
|
||||||
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
|
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
|
||||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
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);
|
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||||
inst->SetEvolveAddToCurrentAmount(1);
|
inst->SetEvolveAddToCurrentAmount(1);
|
||||||
inst->CalculateEvolveProgression();
|
inst->CalculateEvolveProgression();
|
||||||
@@ -187,7 +199,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
database,
|
database,
|
||||||
inst->GetEvolveUniqueID(),
|
inst->GetEvolveUniqueID(),
|
||||||
inst->GetEvolveCurrentAmount(),
|
inst->GetEvolveCurrentAmount(),
|
||||||
inst->GetEvolveProgression());
|
inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -195,7 +208,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||||
|
|
||||||
LogEvolveItem(
|
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}]",
|
"- Increased count by 1 for <green>[{1}]",
|
||||||
sub_type,
|
sub_type,
|
||||||
inst->GetID()
|
inst->GetID()
|
||||||
@@ -208,6 +222,44 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
|
|
||||||
break;
|
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: {
|
default: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +299,7 @@ void Client::DoEvolveItemDisplayFinalResult(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst)
|
bool Client::DoEvolveCheckProgression(EQ::ItemInstance &inst)
|
||||||
{
|
{
|
||||||
if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) {
|
if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -264,6 +316,48 @@ bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RuleB(EvolvingItems, EnableParcelMerchants) &&
|
||||||
|
!RuleB(EvolvingItems, DestroyAugmentsOnEvolve) &&
|
||||||
|
inst.IsAugmented()
|
||||||
|
) {
|
||||||
|
auto const augs = inst.GetAugmentIDs();
|
||||||
|
std::vector<CharacterParcelsRepository::CharacterParcels> parcels;
|
||||||
|
for (auto const &item_id: augs) {
|
||||||
|
if (!item_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterParcelsRepository::CharacterParcels p{};
|
||||||
|
p.char_id = CharacterID();
|
||||||
|
p.from_name = "Evolving Item Sub-System";
|
||||||
|
p.note = fmt::format(
|
||||||
|
"System automatically removed from {} which recently evolved.",
|
||||||
|
inst.GetItem()->Name
|
||||||
|
);
|
||||||
|
p.slot_id = FindNextFreeParcelSlotUsingMemory();
|
||||||
|
p.sent_date = time(nullptr);
|
||||||
|
p.item_id = item_id;
|
||||||
|
p.quantity = 1;
|
||||||
|
|
||||||
|
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_SEND)) {
|
||||||
|
PlayerEvent::ParcelSend e{};
|
||||||
|
e.from_player_name = p.from_name;
|
||||||
|
e.to_player_name = GetCleanName();
|
||||||
|
e.item_id = p.item_id;
|
||||||
|
e.quantity = 1;
|
||||||
|
e.sent_date = p.sent_date;
|
||||||
|
|
||||||
|
RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
parcels.push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterParcelsRepository::InsertMany(database, parcels);
|
||||||
|
SendParcelStatus();
|
||||||
|
SendParcelIconStatus();
|
||||||
|
}
|
||||||
|
|
||||||
CheckItemDiscoverability(new_inst->GetID());
|
CheckItemDiscoverability(new_inst->GetID());
|
||||||
|
|
||||||
PlayerEvent::EvolveItem e{};
|
PlayerEvent::EvolveItem e{};
|
||||||
|
|||||||
@@ -16974,7 +16974,7 @@ void Client::Handle_OP_GuildTributeDonateItem(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
SendGuildTributeDonateItemReply(in, favor);
|
SendGuildTributeDonateItemReply(in, favor);
|
||||||
|
|
||||||
if(player_event_logs.IsEventEnabled(PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM)) {
|
if(inst && player_event_logs.IsEventEnabled(PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM)) {
|
||||||
auto e = PlayerEvent::GuildTributeDonateItem{ .item_id = inst->GetID(),
|
auto e = PlayerEvent::GuildTributeDonateItem{ .item_id = inst->GetID(),
|
||||||
.augment_1_id = inst->GetAugmentItemID(0),
|
.augment_1_id = inst->GetAugmentItemID(0),
|
||||||
.augment_2_id = inst->GetAugmentItemID(1),
|
.augment_2_id = inst->GetAugmentItemID(1),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "../common/types.h"
|
#include "../common/types.h"
|
||||||
#include "../common/spdat.h"
|
#include "../common/spdat.h"
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
|
||||||
#define HIGHEST_RESIST 9 //Max resist type value
|
#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.
|
#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 persistant_buff;
|
||||||
bool client; //True if the caster is a client
|
bool client; //True if the caster is a client
|
||||||
bool UpdateClient;
|
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 {
|
struct StatBonuses {
|
||||||
|
|||||||
+1
-1
@@ -1569,7 +1569,7 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::LOOT_ITEM) && !IsPlayerCorpse()) {
|
if (inst && player_event_logs.IsEventEnabled(PlayerEvent::LOOT_ITEM) && !IsPlayerCorpse()) {
|
||||||
auto e = PlayerEvent::LootItemEvent{
|
auto e = PlayerEvent::LootItemEvent{
|
||||||
.item_id = inst->GetItem()->ID,
|
.item_id = inst->GetItem()->ID,
|
||||||
.item_name = inst->GetItem()->Name,
|
.item_name = inst->GetItem()->Name,
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ public:
|
|||||||
uint32 GetItemIDBySlot(uint16 loot_slot);
|
uint32 GetItemIDBySlot(uint16 loot_slot);
|
||||||
uint16 GetFirstLootSlotByItemID(uint32 item_id);
|
uint16 GetFirstLootSlotByItemID(uint32 item_id);
|
||||||
std::vector<int> GetLootList();
|
std::vector<int> GetLootList();
|
||||||
|
inline const LootItems &GetLootItems() { return m_item_list; }
|
||||||
void LootCorpseItem(Client *c, const EQApplicationPacket *app);
|
void LootCorpseItem(Client *c, const EQApplicationPacket *app);
|
||||||
void EndLoot(Client *c, const EQApplicationPacket *app);
|
void EndLoot(Client *c, const EQApplicationPacket *app);
|
||||||
void MakeLootRequestPackets(Client *c, const EQApplicationPacket *app);
|
void MakeLootRequestPackets(Client *c, const EQApplicationPacket *app);
|
||||||
|
|||||||
+127
-23
@@ -27,7 +27,8 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
|
|||||||
void DataBucket::SetData(const DataBucketKey &k_)
|
void DataBucket::SetData(const DataBucketKey &k_)
|
||||||
{
|
{
|
||||||
DataBucketKey k = k_; // copy the key so we can modify it
|
DataBucketKey k = k_; // copy the key so we can modify it
|
||||||
if (k.key.find(NESTED_KEY_DELIMITER) != std::string::npos) {
|
bool is_nested = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
|
||||||
|
if (is_nested) {
|
||||||
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,10 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
|||||||
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
|
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
|
||||||
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
|
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
|
||||||
}
|
}
|
||||||
|
if (is_nested) {
|
||||||
|
LogDataBuckets("Nested keys can't expire; set expiration on the parent key");
|
||||||
|
expires_time_unix = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.expires = expires_time_unix;
|
b.expires = expires_time_unix;
|
||||||
@@ -75,26 +80,45 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
|||||||
std::string existing_value = r.id > 0 ? r.value : "{}";
|
std::string existing_value = r.id > 0 ? r.value : "{}";
|
||||||
json json_value = json::object();
|
json json_value = json::object();
|
||||||
|
|
||||||
|
// Check if the JSON is valid
|
||||||
|
if (Strings::IsValidJson(existing_value)) {
|
||||||
try {
|
try {
|
||||||
json_value = json::parse(existing_value);
|
json_value = json::parse(existing_value);
|
||||||
} catch (json::parse_error &e) {
|
} catch (json::parse_error &e) {
|
||||||
LogError("Failed to parse JSON for key [{}]: {}", k_.key, e.what());
|
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", k_.key, e.what());
|
||||||
json_value = json::object(); // Reset to an empty object on error
|
json_value = json::object(); // Reset to an empty object on error
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively merge new key-value pair into the JSON object
|
// Recursively merge new key-value pair into the JSON object
|
||||||
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
|
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
|
||||||
|
auto top_key = nested_keys.front();
|
||||||
|
// remove the top-level key
|
||||||
|
nested_keys.erase(nested_keys.begin());
|
||||||
|
|
||||||
json *current = &json_value;
|
json *current = &json_value;
|
||||||
|
|
||||||
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
||||||
const std::string &key_part = nested_keys[i];
|
const std::string &key_part = nested_keys[i];
|
||||||
|
|
||||||
if (i == nested_keys.size() - 1) {
|
if (i == nested_keys.size() - 1) {
|
||||||
|
|
||||||
|
LogDataBucketsDetail("Setting key [{}] key_part [{}]", k.key, key_part);
|
||||||
|
|
||||||
|
// If the key already exists and is an object or array, prevent overwriting to avoid data loss
|
||||||
|
if (current->contains(key_part) &&
|
||||||
|
((*current)[key_part].is_object() || (*current)[key_part].is_array())) {
|
||||||
|
LogDataBuckets("Attempted to overwrite an existing object or array at key [{}] - skipping", k_.key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the value at the final key
|
// Set the value at the final key
|
||||||
(*current)[key_part] = k_.value;
|
(*current)[key_part] = k_.value;
|
||||||
} else {
|
} else {
|
||||||
// Traverse or create nested objects
|
// Traverse or create nested objects
|
||||||
if (!current->contains(key_part)) {
|
if (!current->contains(key_part)) {
|
||||||
(*current)[key_part] = json::object();
|
(*current)[key_part] = json::object();
|
||||||
|
LogDataBucketsDetail("Creating nested root key [{}] key_part [{}]", k.key, key_part);
|
||||||
} else if (!(*current)[key_part].is_object()) {
|
} else if (!(*current)[key_part].is_object()) {
|
||||||
// If key exists but is not an object, reset to object to avoid conflicts
|
// If key exists but is not an object, reset to object to avoid conflicts
|
||||||
(*current)[key_part] = json::object();
|
(*current)[key_part] = json::object();
|
||||||
@@ -105,7 +129,7 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
|||||||
|
|
||||||
// Serialize JSON back to string
|
// Serialize JSON back to string
|
||||||
b.value = json_value.dump();
|
b.value = json_value.dump();
|
||||||
b.key_ = nested_keys.front(); // Use the top-level key
|
b.key_ = top_key; // Use the top-level key
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bucket_id) {
|
if (bucket_id) {
|
||||||
@@ -142,12 +166,20 @@ DataBucketsRepository::DataBuckets DataBucket::ExtractNestedValue(
|
|||||||
const std::string &full_key)
|
const std::string &full_key)
|
||||||
{
|
{
|
||||||
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
|
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
|
||||||
|
auto top_key = nested_keys.front();
|
||||||
|
nested_keys.erase(nested_keys.begin());
|
||||||
json json_value;
|
json json_value;
|
||||||
|
|
||||||
|
// Check if the JSON is valid
|
||||||
|
if (!Strings::IsValidJson(bucket.value)) {
|
||||||
|
LogDataBuckets("Invalid JSON for key [{}]", bucket.key_);
|
||||||
|
return DataBucketsRepository::NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
json_value = json::parse(bucket.value); // Parse the JSON
|
json_value = json::parse(bucket.value); // Parse the JSON
|
||||||
} catch (json::parse_error &ex) {
|
} catch (json::parse_error &ex) {
|
||||||
LogError("Failed to parse JSON for key [{}]: {}", bucket.key_, ex.what());
|
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", bucket.key_, ex.what());
|
||||||
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
|
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,9 +368,11 @@ bool DataBucket::GetDataBuckets(Mob *mob)
|
|||||||
|
|
||||||
bool DataBucket::DeleteData(const DataBucketKey &k)
|
bool DataBucket::DeleteData(const DataBucketKey &k)
|
||||||
{
|
{
|
||||||
if (CanCache(k)) {
|
bool is_nested_key = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
|
||||||
|
|
||||||
|
if (!is_nested_key) {
|
||||||
|
// Update cache
|
||||||
|
if (CanCache(k)) {
|
||||||
// delete from cache where contents match
|
// delete from cache where contents match
|
||||||
g_data_bucket_cache.erase(
|
g_data_bucket_cache.erase(
|
||||||
std::remove_if(
|
std::remove_if(
|
||||||
@@ -350,32 +384,102 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
|
|||||||
),
|
),
|
||||||
g_data_bucket_cache.end()
|
g_data_bucket_cache.end()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
LogDataBuckets(
|
// Regular key deletion, no nesting involved
|
||||||
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}] cache size before [{}] after [{}]",
|
return DataBucketsRepository::DeleteWhere(
|
||||||
k.key,
|
database,
|
||||||
k.bot_id,
|
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), k.key)
|
||||||
k.account_id,
|
);
|
||||||
k.character_id,
|
}
|
||||||
k.npc_id,
|
|
||||||
k.bot_id,
|
// If it's a nested key, retrieve the top-level JSON object
|
||||||
k.zone_id,
|
auto top_level_key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
||||||
k.instance_id,
|
DataBucketKey top_level_k = k;
|
||||||
size_before,
|
top_level_k.key = top_level_key;
|
||||||
g_data_bucket_cache.size()
|
|
||||||
|
auto r = GetData(top_level_k);
|
||||||
|
if (r.id == 0 || r.value.empty() || !Strings::IsValidJson(r.value)) {
|
||||||
|
LogDataBuckets("Attempted to delete nested key [{}] but parent key [{}] does not exist or is invalid JSON", k.key, top_level_key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
json json_value;
|
||||||
|
try {
|
||||||
|
json_value = json::parse(r.value);
|
||||||
|
} catch (json::parse_error &ex) {
|
||||||
|
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", top_level_key, ex.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively remove the nested key
|
||||||
|
auto nested_keys = Strings::Split(k.key, NESTED_KEY_DELIMITER);
|
||||||
|
auto top_key = nested_keys.front();
|
||||||
|
nested_keys.erase(nested_keys.begin());
|
||||||
|
json *current = &json_value;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
||||||
|
const std::string &key_part = nested_keys[i];
|
||||||
|
|
||||||
|
if (i == nested_keys.size() - 1) {
|
||||||
|
// Last key in the hierarchy - delete it
|
||||||
|
if (current->contains(key_part)) {
|
||||||
|
current->erase(key_part);
|
||||||
|
LogDataBuckets("Deleted nested key [{}] from [{}]", key_part, k.key);
|
||||||
|
} else {
|
||||||
|
LogDataBuckets("Key [{}] not found in JSON - nothing to delete", k.key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!current->contains(key_part) || !(*current)[key_part].is_object()) {
|
||||||
|
LogDataBuckets("Parent key [{}] does not exist or is not an object", key_part);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
current = &(*current)[key_part];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the JSON object is now empty, delete the top-level key
|
||||||
|
if (json_value.empty()) {
|
||||||
|
LogDataBuckets("Top-level key [{}] is now empty, deleting entire entry", top_level_key);
|
||||||
|
|
||||||
|
// delete cache
|
||||||
|
if (CanCache(k)) {
|
||||||
|
g_data_bucket_cache.erase(
|
||||||
|
std::remove_if(
|
||||||
|
g_data_bucket_cache.begin(),
|
||||||
|
g_data_bucket_cache.end(),
|
||||||
|
[&](DataBucketsRepository::DataBuckets &e) {
|
||||||
|
return CheckBucketMatch(e, top_level_k);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
g_data_bucket_cache.end()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DataBucketsRepository::DeleteWhere(
|
return DataBucketsRepository::DeleteWhere(
|
||||||
database,
|
database,
|
||||||
fmt::format(
|
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), top_level_key)
|
||||||
"{} `key` = '{}'",
|
|
||||||
DataBucket::GetScopedDbFilters(k),
|
|
||||||
k.key
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise, update the existing JSON without the deleted key
|
||||||
|
r.value = json_value.dump();
|
||||||
|
DataBucketsRepository::UpdateOne(database, r);
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
if (CanCache(k)) {
|
||||||
|
for (auto &e : g_data_bucket_cache) {
|
||||||
|
if (CheckBucketMatch(e, top_level_k)) {
|
||||||
|
e.value = r.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
||||||
{
|
{
|
||||||
LogDataBuckets(
|
LogDataBuckets(
|
||||||
|
|||||||
+44
-1
@@ -25,6 +25,7 @@
|
|||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
#include "../common/repositories/character_expedition_lockouts_repository.h"
|
#include "../common/repositories/character_expedition_lockouts_repository.h"
|
||||||
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
||||||
|
#include <cereal/types/utility.hpp>
|
||||||
|
|
||||||
extern WorldServer worldserver;
|
extern WorldServer worldserver;
|
||||||
|
|
||||||
@@ -162,14 +163,28 @@ void DynamicZone::CacheAllFromDatabase()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dz->UpdateMembers();
|
|
||||||
zone->dynamic_zone_cache.emplace(dz_id, std::move(dz));
|
zone->dynamic_zone_cache.emplace(dz_id, std::move(dz));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!zone->dynamic_zone_cache.empty())
|
||||||
|
{
|
||||||
|
RequestMemberStatuses();
|
||||||
|
}
|
||||||
|
|
||||||
LogInfo("Loaded [{}] dynamic zone(s)", Strings::Commify(zone->dynamic_zone_cache.size()));
|
LogInfo("Loaded [{}] dynamic zone(s)", Strings::Commify(zone->dynamic_zone_cache.size()));
|
||||||
LogDynamicZones("Caching [{}] dynamic zone(s) took [{}s]", zone->dynamic_zone_cache.size(), bench.elapsed());
|
LogDynamicZones("Caching [{}] dynamic zone(s) took [{}s]", zone->dynamic_zone_cache.size(), bench.elapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DynamicZone::RequestMemberStatuses()
|
||||||
|
{
|
||||||
|
ServerPacket pack(ServerOP_DzGetBulkMemberStatuses, sizeof(ServerDzCerealData_Struct));
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack.pBuffer);
|
||||||
|
buf->zone_id = static_cast<uint16_t>(zone->GetZoneID());
|
||||||
|
buf->inst_id = static_cast<uint16_t>(zone->GetInstanceID());
|
||||||
|
|
||||||
|
worldserver.SendPacket(&pack);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
DynamicZone* FindDynamicZone(T pred)
|
DynamicZone* FindDynamicZone(T pred)
|
||||||
{
|
{
|
||||||
@@ -849,6 +864,34 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
|
{
|
||||||
|
if (zone)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint32_t, std::vector<DynamicZoneMember>>> dzs;
|
||||||
|
dzs.reserve(zone->dynamic_zone_cache.size());
|
||||||
|
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack->pBuffer);
|
||||||
|
EQ::Util::MemoryStreamReader ss(buf->cereal_data, buf->cereal_size);
|
||||||
|
{
|
||||||
|
cereal::BinaryInputArchive archive(ss);
|
||||||
|
archive(dzs);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [dz_id, members] : dzs)
|
||||||
|
{
|
||||||
|
if (auto dz = DynamicZone::FindDynamicZoneByID(dz_id))
|
||||||
|
{
|
||||||
|
for (const auto& member : members)
|
||||||
|
{
|
||||||
|
dz->SetInternalMemberStatus(member.id, member.status);
|
||||||
|
}
|
||||||
|
dz->m_has_member_statuses = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ServerOP_DzUpdateMemberStatus:
|
case ServerOP_DzUpdateMemberStatus:
|
||||||
{
|
{
|
||||||
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
||||||
|
|||||||
+2
-1
@@ -92,13 +92,13 @@ public:
|
|||||||
void SendMemberNameToZoneMembers(const std::string& char_name, bool remove);
|
void SendMemberNameToZoneMembers(const std::string& char_name, bool remove);
|
||||||
void SendMemberStatusToZoneMembers(const DynamicZoneMember& member);
|
void SendMemberStatusToZoneMembers(const DynamicZoneMember& member);
|
||||||
void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow);
|
void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow);
|
||||||
void UpdateMembers();
|
|
||||||
|
|
||||||
std::string GetLootEvent(uint32_t id, DzLootEvent::Type type) const;
|
std::string GetLootEvent(uint32_t id, DzLootEvent::Type type) const;
|
||||||
void SetLootEvent(uint32_t id, const std::string& event, DzLootEvent::Type type);
|
void SetLootEvent(uint32_t id, const std::string& event, DzLootEvent::Type type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void StartAllClientRemovalTimers();
|
static void StartAllClientRemovalTimers();
|
||||||
|
static void RequestMemberStatuses();
|
||||||
|
|
||||||
uint16_t GetCurrentInstanceID() const override;
|
uint16_t GetCurrentInstanceID() const override;
|
||||||
uint16_t GetCurrentZoneID() const override;
|
uint16_t GetCurrentZoneID() const override;
|
||||||
@@ -125,6 +125,7 @@ private:
|
|||||||
void SendWorldPlayerInvite(const std::string& inviter, const std::string& swap_name, const std::string& add_name, bool pending = false);
|
void SendWorldPlayerInvite(const std::string& inviter, const std::string& swap_name, const std::string& add_name, bool pending = false);
|
||||||
void SetUpdatedDuration(uint32_t seconds);
|
void SetUpdatedDuration(uint32_t seconds);
|
||||||
void TryAddClient(Client* add_client, const std::string& inviter, const std::string& swap_name, Client* leader = nullptr);
|
void TryAddClient(Client* add_client, const std::string& inviter, const std::string& swap_name, Client* leader = nullptr);
|
||||||
|
void UpdateMembers();
|
||||||
|
|
||||||
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
|
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
|
||||||
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);
|
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);
|
||||||
|
|||||||
+24
-1
@@ -754,7 +754,7 @@ void Perl__setsky(uint8 new_sky)
|
|||||||
|
|
||||||
void Perl__setguild(uint32_t guild_id, uint8_t guild_rank_id)
|
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)
|
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);
|
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()
|
void perl_register_quest()
|
||||||
{
|
{
|
||||||
perl::interpreter perl(PERL_GET_THX);
|
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))&Perl__gmsay);
|
||||||
package.add("gmsay", (void(*)(const char*, int, bool, int))&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("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("has_zone_flag", &Perl__has_zone_flag);
|
||||||
package.add("hasrecipelearned", &Perl__hasrecipelearned);
|
package.add("hasrecipelearned", &Perl__hasrecipelearned);
|
||||||
package.add("hastimer", &Perl__hastimer);
|
package.add("hastimer", &Perl__hastimer);
|
||||||
|
|||||||
+2
-2
@@ -2914,7 +2914,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanning_mob->GetID() <= 0) {
|
if (scanning_mob->GetID() <= 0 || scanning_mob->IsZoneController()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2933,7 +2933,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
|||||||
for (auto &e : mob_list) {
|
for (auto &e : mob_list) {
|
||||||
auto mob = e.second;
|
auto mob = e.second;
|
||||||
|
|
||||||
if (mob->GetID() <= 0) {
|
if (mob && (mob->GetID() <= 0 || mob->IsZoneController())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ void command_guild(Client* c, const Seperator* sep)
|
|||||||
else {
|
else {
|
||||||
auto guild_name = sep->argplus[3];
|
auto guild_name = sep->argplus[3];
|
||||||
auto guild_id = guild_mgr.CreateGuild(sep->argplus[3], leader_id);
|
auto guild_id = guild_mgr.CreateGuild(sep->argplus[3], leader_id);
|
||||||
|
auto leader = entity_list.GetClientByCharID(leader_id);
|
||||||
|
|
||||||
|
|
||||||
LogGuilds(
|
LogGuilds(
|
||||||
"[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]",
|
"[{}]: 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()
|
).c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!guild_mgr.SetGuild(leader_id, guild_id, GUILD_LEADER)) {
|
if (!guild_mgr.SetGuild(leader, guild_id, GUILD_LEADER)) {
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -268,6 +270,7 @@ void command_guild(Client* c, const Seperator* sep)
|
|||||||
database.GetCharacterID(sep->arg[2])
|
database.GetCharacterID(sep->arg[2])
|
||||||
);
|
);
|
||||||
auto character_name = database.GetCharNameByID(character_id);
|
auto character_name = database.GetCharNameByID(character_id);
|
||||||
|
auto client = entity_list.GetClientByCharID(character_id);
|
||||||
if (!character_id || character_name.empty()) {
|
if (!character_id || character_name.empty()) {
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
@@ -305,14 +308,14 @@ void command_guild(Client* c, const Seperator* sep)
|
|||||||
"{} ({}) has {} put into {} ({}).",
|
"{} ({}) has {} put into {} ({}).",
|
||||||
character_name,
|
character_name,
|
||||||
character_id,
|
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_mgr.GetGuildNameByID(guild_id),
|
||||||
guild_id
|
guild_id
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
guild_mgr.SetGuild(character_id, GUILD_NONE, 0);
|
guild_mgr.SetGuild(client, GUILD_NONE, 0);
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
|||||||
send_to_client.at(0).character_name.c_str()
|
send_to_client.at(0).character_name.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_SEND)) {
|
if (inst && player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_SEND)) {
|
||||||
PlayerEvent::ParcelSend e{};
|
PlayerEvent::ParcelSend e{};
|
||||||
e.from_player_name = parcel_out.from_name;
|
e.from_player_name = parcel_out.from_name;
|
||||||
e.to_player_name = send_to_client.at(0).character_name;
|
e.to_player_name = send_to_client.at(0).character_name;
|
||||||
@@ -281,7 +281,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
|||||||
send_to_client.at(0).character_name.c_str()
|
send_to_client.at(0).character_name.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_SEND)) {
|
if (inst && player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_SEND)) {
|
||||||
PlayerEvent::ParcelSend e{};
|
PlayerEvent::ParcelSend e{};
|
||||||
e.from_player_name = parcel_out.from_name;
|
e.from_player_name = parcel_out.from_name;
|
||||||
e.to_player_name = send_to_client.at(0).character_name;
|
e.to_player_name = send_to_client.at(0).character_name;
|
||||||
|
|||||||
+35
-36
@@ -1317,42 +1317,6 @@ bool GuildBankManager::SplitStack(uint32 guild_id, uint16 slot_id, uint32 quanti
|
|||||||
return true;
|
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)
|
int32 GuildBankManager::NextFreeBankSlot(uint32 guild_id, uint32 area)
|
||||||
{
|
{
|
||||||
auto guild_bank = GetGuildBank(guild_id);
|
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);
|
entity_list.QueueClientsGuildBankItemUpdate(&gbius, guild_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ZoneGuildManager::SetGuild(Client *client, uint32 guild_id, uint8 rank)
|
||||||
|
{
|
||||||
|
if (!client || rank > GUILD_MAX_RANK || !GetGuildByGuildID(guild_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rank <= GUILD_RANK_NONE) {
|
||||||
|
rank = GUILD_RECRUIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32 current_guild_id = client->GuildID();
|
||||||
|
if (current_guild_id == guild_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_guild_id != guild_id && current_guild_id != GUILD_NONE) {
|
||||||
|
guild_mgr.RemoveMember(client->GuildID(), client->CharacterID(), std::string(client->GetCleanName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
client->SetGuildID(guild_id);
|
||||||
|
client->SetGuildRank(rank);
|
||||||
|
MemberAdd(
|
||||||
|
guild_id,
|
||||||
|
client->CharacterID(),
|
||||||
|
client->GetLevel(),
|
||||||
|
client->GetClass(),
|
||||||
|
rank,
|
||||||
|
client->GetZoneID(),
|
||||||
|
client->GetName()
|
||||||
|
);
|
||||||
|
|
||||||
|
client->SendGuildSpawnAppearance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public:
|
|||||||
void ListGuilds(Client *c, uint32 guild_id = 0) const;
|
void ListGuilds(Client *c, uint32 guild_id = 0) const;
|
||||||
void DescribeGuild(Client *c, uint32 guild_id) const;
|
void DescribeGuild(Client *c, uint32 guild_id) const;
|
||||||
bool IsActionABankAction(GuildAction action);
|
bool IsActionABankAction(GuildAction action);
|
||||||
|
bool SetGuild(Client *client, uint32 guild_id, uint8 rank);
|
||||||
|
|
||||||
uint8 *MakeGuildMembers(uint32 guild_id, const char* prefix_name, uint32& length);
|
uint8 *MakeGuildMembers(uint32 guild_id, const char* prefix_name, uint32& length);
|
||||||
void SendToWorldMemberLevelUpdate(uint32 guild_id, uint32 level, std::string player_name);
|
void SendToWorldMemberLevelUpdate(uint32 guild_id, uint32 level, std::string player_name);
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,7 @@ const NPCType *Horse::BuildHorseType(uint16 spell_id)
|
|||||||
n->npc_id = 0;
|
n->npc_id = 0;
|
||||||
n->loottable_id = 0;
|
n->loottable_id = 0;
|
||||||
n->texture = e.texture;
|
n->texture = e.texture;
|
||||||
n->helmtexture = e.texture;
|
n->helmtexture = e.helmtexture == -1 ? e.texture : e.helmtexture;
|
||||||
n->runspeed = e.mountspeed;
|
n->runspeed = e.mountspeed;
|
||||||
n->light = 0;
|
n->light = 0;
|
||||||
n->STR = 75;
|
n->STR = 75;
|
||||||
|
|||||||
+2
-2
@@ -763,7 +763,7 @@ void Client::DropItem(int16 slot_id, bool recurse)
|
|||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::DROPPED_ITEM)) {
|
if (inst && player_event_logs.IsEventEnabled(PlayerEvent::DROPPED_ITEM)) {
|
||||||
auto e = PlayerEvent::DroppedItemEvent{
|
auto e = PlayerEvent::DroppedItemEvent{
|
||||||
.item_id = inst->GetID(),
|
.item_id = inst->GetID(),
|
||||||
.augment_1_id = inst->GetAugmentItemID(0),
|
.augment_1_id = inst->GetAugmentItemID(0),
|
||||||
@@ -1655,7 +1655,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
|||||||
|
|
||||||
DeleteItemInInventory(EQ::invslot::slotCursor, 0, true);
|
DeleteItemInInventory(EQ::invslot::slotCursor, 0, true);
|
||||||
|
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_DESTROY)) {
|
if (test_inst && player_event_logs.IsEventEnabled(PlayerEvent::ITEM_DESTROY)) {
|
||||||
auto e = PlayerEvent::DestroyItemEvent{
|
auto e = PlayerEvent::DestroyItemEvent{
|
||||||
.item_id = test_inst->GetItem()->ID,
|
.item_id = test_inst->GetItem()->ID,
|
||||||
.item_name = test_inst->GetItem()->Name,
|
.item_name = test_inst->GetItem()->Name,
|
||||||
|
|||||||
+14
-1
@@ -276,6 +276,11 @@ void NPC::AddLootDrop(
|
|||||||
uint32 augment_six
|
uint32 augment_six
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (m_resumed_from_zone_suspend) {
|
||||||
|
LogZoneState("NPC [{}] is resuming from zone suspend, skipping AddItem", GetCleanName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!item2) {
|
if (!item2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -370,6 +375,10 @@ void NPC::AddLootDrop(
|
|||||||
if (item2->Slots & slots) {
|
if (item2->Slots & slots) {
|
||||||
if (equipment[i]) {
|
if (equipment[i]) {
|
||||||
compitem = database.GetItem(equipment[i]);
|
compitem = database.GetItem(equipment[i]);
|
||||||
|
if (!compitem) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item2->AC > compitem->AC || (item2->AC == compitem->AC && item2->HP > compitem->HP)) {
|
if (item2->AC > compitem->AC || (item2->AC == compitem->AC && item2->HP > compitem->HP)) {
|
||||||
// item would be an upgrade
|
// item would be an upgrade
|
||||||
// check if we're multi-slot, if yes then we have to keep
|
// check if we're multi-slot, if yes then we have to keep
|
||||||
@@ -380,6 +389,9 @@ void NPC::AddLootDrop(
|
|||||||
else {
|
else {
|
||||||
// Unequip old item
|
// Unequip old item
|
||||||
auto *old_item = GetItem(i);
|
auto *old_item = GetItem(i);
|
||||||
|
if (!old_item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
old_item->equip_slot = EQ::invslot::SLOT_INVALID;
|
old_item->equip_slot = EQ::invslot::SLOT_INVALID;
|
||||||
|
|
||||||
@@ -500,6 +512,7 @@ void NPC::AddLootDrop(
|
|||||||
parse->EventNPC(EVENT_LOOT_ADDED, this, nullptr, "", 0, &args);
|
parse->EventNPC(EVENT_LOOT_ADDED, this, nullptr, "", 0, &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item->lootdrop_id = loot_drop.lootdrop_id;
|
||||||
m_loot_items.push_back(item);
|
m_loot_items.push_back(item);
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
@@ -671,7 +684,7 @@ LootItem *NPC::GetItem(int slot_id)
|
|||||||
end = m_loot_items.end();
|
end = m_loot_items.end();
|
||||||
for (; cur != end; ++cur) {
|
for (; cur != end; ++cur) {
|
||||||
LootItem *item = *cur;
|
LootItem *item = *cur;
|
||||||
if (item->equip_slot == slot_id) {
|
if (item && item->equip_slot == slot_id) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-11
@@ -1199,16 +1199,6 @@ void Lua_Client::SetStartZone(int zone_id, float x, float y, float z) {
|
|||||||
self->SetStartZone(zone_id, x, y, z);
|
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) {
|
void Lua_Client::AddPVPPoints(uint32 points) {
|
||||||
Lua_Safe_Call_Void();
|
Lua_Safe_Call_Void();
|
||||||
self->AddPVPPoints(points);
|
self->AddPVPPoints(points);
|
||||||
@@ -3548,6 +3538,34 @@ std::string Lua_Client::GetPotionBeltItemName(uint8 slot_id)
|
|||||||
return self->GetPotionBeltItemName(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() {
|
luabind::scope lua_register_client() {
|
||||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -3869,8 +3887,11 @@ luabind::scope lua_register_client() {
|
|||||||
.def("IsTaskActive", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskActive)
|
.def("IsTaskActive", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskActive)
|
||||||
.def("IsTaskActivityActive", (bool(Lua_Client::*)(int,int))&Lua_Client::IsTaskActivityActive)
|
.def("IsTaskActivityActive", (bool(Lua_Client::*)(int,int))&Lua_Client::IsTaskActivityActive)
|
||||||
.def("IsTaskCompleted", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskCompleted)
|
.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("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("Kick", (void(Lua_Client::*)(void))&Lua_Client::Kick)
|
||||||
.def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines)
|
.def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines)
|
||||||
.def("LearnRecipe", (void(Lua_Client::*)(uint32))&Lua_Client::LearnRecipe)
|
.def("LearnRecipe", (void(Lua_Client::*)(uint32))&Lua_Client::LearnRecipe)
|
||||||
|
|||||||
+5
-2
@@ -313,8 +313,6 @@ public:
|
|||||||
void SetStartZone(int zone_id, float x, float y);
|
void SetStartZone(int zone_id, float x, float y);
|
||||||
void SetStartZone(int zone_id, float x, float y, float z);
|
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 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 AddPVPPoints(uint32 points);
|
||||||
void AddCrystals(uint32 radiant_count, uint32 ebon_count);
|
void AddCrystals(uint32 radiant_count, uint32 ebon_count);
|
||||||
void SetEbonCrystals(uint32 value);
|
void SetEbonCrystals(uint32 value);
|
||||||
@@ -518,6 +516,11 @@ public:
|
|||||||
uint32 GetPotionBeltItemIcon(uint8 slot_id);
|
uint32 GetPotionBeltItemIcon(uint8 slot_id);
|
||||||
uint32 GetPotionBeltItemID(uint8 slot_id);
|
uint32 GetPotionBeltItemID(uint8 slot_id);
|
||||||
std::string GetPotionBeltItemName(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
|
// account data buckets
|
||||||
void SetAccountBucket(std::string bucket_name, std::string bucket_value);
|
void SetAccountBucket(std::string bucket_name, std::string bucket_value);
|
||||||
|
|||||||
+27
-1
@@ -468,7 +468,7 @@ void lua_set_sky(int sky) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lua_set_guild(int guild_id, int rank) {
|
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) {
|
void lua_create_guild(const char *name, const char *leader) {
|
||||||
@@ -5642,6 +5642,31 @@ Lua_Zone lua_get_zone()
|
|||||||
return Lua_Zone(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 { \
|
#define LuaCreateNPCParse(name, c_type, default_value) do { \
|
||||||
cur = table[#name]; \
|
cur = table[#name]; \
|
||||||
if(luabind::type(cur) != LUA_TNIL) { \
|
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_circle", &lua_spawn_circle),
|
||||||
luabind::def("spawn_grid", &lua_spawn_grid),
|
luabind::def("spawn_grid", &lua_spawn_grid),
|
||||||
luabind::def("get_zone", &lua_get_zone),
|
luabind::def("get_zone", &lua_get_zone),
|
||||||
|
luabind::def("handin", &lua_handin),
|
||||||
/*
|
/*
|
||||||
Cross Zone
|
Cross Zone
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -939,6 +939,12 @@ Lua_Spawn Lua_NPC::GetSpawn(lua_State* L)
|
|||||||
return Lua_Spawn(self->GetSpawn());
|
return Lua_Spawn(self->GetSpawn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Lua_NPC::IsResumedFromZoneSuspend()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->IsResumedFromZoneSuspend();
|
||||||
|
}
|
||||||
|
|
||||||
luabind::scope lua_register_npc() {
|
luabind::scope lua_register_npc() {
|
||||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -1040,6 +1046,7 @@ luabind::scope lua_register_npc() {
|
|||||||
.def("IsOnHatelist", (bool(Lua_NPC::*)(Lua_Mob))&Lua_NPC::IsOnHatelist)
|
.def("IsOnHatelist", (bool(Lua_NPC::*)(Lua_Mob))&Lua_NPC::IsOnHatelist)
|
||||||
.def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget)
|
.def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget)
|
||||||
.def("IsRareSpawn", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRareSpawn)
|
.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("IsTaunting", (bool(Lua_NPC::*)(void))&Lua_NPC::IsTaunting)
|
||||||
.def("IsUnderwaterOnly", (bool(Lua_NPC::*)(void))&Lua_NPC::IsUnderwaterOnly)
|
.def("IsUnderwaterOnly", (bool(Lua_NPC::*)(void))&Lua_NPC::IsUnderwaterOnly)
|
||||||
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
|
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ public:
|
|||||||
);
|
);
|
||||||
void ReturnHandinItems(Lua_Client c);
|
void ReturnHandinItems(Lua_Client c);
|
||||||
Lua_Spawn GetSpawn(lua_State* L);
|
Lua_Spawn GetSpawn(lua_State* L);
|
||||||
|
bool IsResumedFromZoneSuspend();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+53
-1
@@ -727,6 +727,52 @@ std::string Lua_Zone::GetBucketRemaining(const std::string& bucket_name)
|
|||||||
return self->GetBucketRemaining(bucket_name);
|
return self->GetBucketRemaining(bucket_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lua_Zone::ClearVariables()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->ClearVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Zone::DeleteVariable(const std::string& variable_name)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->DeleteVariable(variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Lua_Zone::GetVariable(const std::string& variable_name)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_String();
|
||||||
|
return self->GetVariable(variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
luabind::object Lua_Zone::GetVariables(lua_State* L)
|
||||||
|
{
|
||||||
|
auto t = luabind::newtable(L);
|
||||||
|
if (d_) {
|
||||||
|
auto self = reinterpret_cast<NativeType*>(d_);
|
||||||
|
auto l = self->GetVariables();
|
||||||
|
int i = 1;
|
||||||
|
for (const auto& v : l) {
|
||||||
|
t[i] = v;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_Zone::SetVariable(const std::string& variable_name, const std::string& variable_value)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->SetVariable(variable_name, variable_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Zone::VariableExists(const std::string& variable_name)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->VariableExists(variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
luabind::scope lua_register_zone() {
|
luabind::scope lua_register_zone() {
|
||||||
return luabind::class_<Lua_Zone>("Zones")
|
return luabind::class_<Lua_Zone>("Zones")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -737,7 +783,9 @@ luabind::scope lua_register_zone() {
|
|||||||
.def("CanDoCombat", &Lua_Zone::CanDoCombat)
|
.def("CanDoCombat", &Lua_Zone::CanDoCombat)
|
||||||
.def("CanLevitate", &Lua_Zone::CanLevitate)
|
.def("CanLevitate", &Lua_Zone::CanLevitate)
|
||||||
.def("ClearSpawnTimers", &Lua_Zone::ClearSpawnTimers)
|
.def("ClearSpawnTimers", &Lua_Zone::ClearSpawnTimers)
|
||||||
|
.def("ClearVariables", &Lua_Zone::ClearVariables)
|
||||||
.def("DeleteBucket", (void(Lua_Zone::*)(const std::string&))&Lua_Zone::DeleteBucket)
|
.def("DeleteBucket", (void(Lua_Zone::*)(const std::string&))&Lua_Zone::DeleteBucket)
|
||||||
|
.def("DeleteVariable", &Lua_Zone::DeleteVariable)
|
||||||
.def("Depop", (void(Lua_Zone::*)(void))&Lua_Zone::Depop)
|
.def("Depop", (void(Lua_Zone::*)(void))&Lua_Zone::Depop)
|
||||||
.def("Depop", (void(Lua_Zone::*)(bool))&Lua_Zone::Depop)
|
.def("Depop", (void(Lua_Zone::*)(bool))&Lua_Zone::Depop)
|
||||||
.def("Despawn", &Lua_Zone::Despawn)
|
.def("Despawn", &Lua_Zone::Despawn)
|
||||||
@@ -819,6 +867,8 @@ luabind::scope lua_register_zone() {
|
|||||||
.def("GetZoneType", &Lua_Zone::GetZoneType)
|
.def("GetZoneType", &Lua_Zone::GetZoneType)
|
||||||
.def("GetUnderworld", &Lua_Zone::GetUnderworld)
|
.def("GetUnderworld", &Lua_Zone::GetUnderworld)
|
||||||
.def("GetUnderworldTeleportIndex", &Lua_Zone::GetUnderworldTeleportIndex)
|
.def("GetUnderworldTeleportIndex", &Lua_Zone::GetUnderworldTeleportIndex)
|
||||||
|
.def("GetVariable", &Lua_Zone::GetVariable)
|
||||||
|
.def("GetVariables", &Lua_Zone::GetVariables)
|
||||||
.def("GetWalkSpeed", &Lua_Zone::GetWalkSpeed)
|
.def("GetWalkSpeed", &Lua_Zone::GetWalkSpeed)
|
||||||
.def("GetZoneZType", &Lua_Zone::GetZoneZType)
|
.def("GetZoneZType", &Lua_Zone::GetZoneZType)
|
||||||
.def("GetZoneTotalBlockedSpells", &Lua_Zone::GetZoneTotalBlockedSpells)
|
.def("GetZoneTotalBlockedSpells", &Lua_Zone::GetZoneTotalBlockedSpells)
|
||||||
@@ -849,6 +899,8 @@ luabind::scope lua_register_zone() {
|
|||||||
.def("SetInstanceTimer", &Lua_Zone::SetInstanceTimer)
|
.def("SetInstanceTimer", &Lua_Zone::SetInstanceTimer)
|
||||||
.def("SetInstanceTimeRemaining", &Lua_Zone::SetInstanceTimeRemaining)
|
.def("SetInstanceTimeRemaining", &Lua_Zone::SetInstanceTimeRemaining)
|
||||||
.def("SetIsHotzone", &Lua_Zone::SetIsHotzone)
|
.def("SetIsHotzone", &Lua_Zone::SetIsHotzone)
|
||||||
.def("ShowZoneGlobalLoot", &Lua_Zone::ShowZoneGlobalLoot);
|
.def("SetVariable", &Lua_Zone::SetVariable)
|
||||||
|
.def("ShowZoneGlobalLoot", &Lua_Zone::ShowZoneGlobalLoot)
|
||||||
|
.def("VariableExists", &Lua_Zone::VariableExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,12 @@ public:
|
|||||||
void SetInstanceTimeRemaining(uint32 time_remaining);
|
void SetInstanceTimeRemaining(uint32 time_remaining);
|
||||||
void SetIsHotzone(bool is_hotzone);
|
void SetIsHotzone(bool is_hotzone);
|
||||||
void ShowZoneGlobalLoot(Lua_Client c);
|
void ShowZoneGlobalLoot(Lua_Client c);
|
||||||
|
void ClearVariables();
|
||||||
|
bool DeleteVariable(const std::string& variable_name);
|
||||||
|
std::string GetVariable(const std::string& variable_name);
|
||||||
|
luabind::object GetVariables(lua_State* L);
|
||||||
|
void SetVariable(const std::string& variable_name, const std::string& variable_value);
|
||||||
|
bool VariableExists(const std::string& variable_name);
|
||||||
|
|
||||||
// data buckets
|
// data buckets
|
||||||
void SetBucket(const std::string& bucket_name, const std::string& bucket_value);
|
void SetBucket(const std::string& bucket_name, const std::string& bucket_value);
|
||||||
|
|||||||
+28
-20
@@ -306,7 +306,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
LogSys.SetDatabase(&database)
|
LogSys.SetDatabase(&database)
|
||||||
->SetLogPath(path.GetLogPath())
|
->SetLogPath(path.GetLogPath())
|
||||||
->LoadLogDatabaseSettings()
|
->LoadLogDatabaseSettings(ZoneCLI::RanTestCommand(argc, argv))
|
||||||
->SetGMSayHandler(&Zone::GMSayHookCallBackProcess)
|
->SetGMSayHandler(&Zone::GMSayHookCallBackProcess)
|
||||||
->StartFileLogs();
|
->StartFileLogs();
|
||||||
|
|
||||||
@@ -631,7 +631,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
if (zone) {
|
if (zone) {
|
||||||
if (!zone->Process()) {
|
if (!zone->Process()) {
|
||||||
Zone::Shutdown();
|
zone->Shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ int main(int argc, char **argv)
|
|||||||
safe_delete(Config);
|
safe_delete(Config);
|
||||||
|
|
||||||
if (zone != 0) {
|
if (zone != 0) {
|
||||||
Zone::Shutdown(true);
|
zone->Shutdown(true);
|
||||||
}
|
}
|
||||||
//Fix for Linux world server problem.
|
//Fix for Linux world server problem.
|
||||||
safe_delete(task_manager);
|
safe_delete(task_manager);
|
||||||
@@ -687,7 +687,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
{
|
{
|
||||||
Zone::Shutdown(true);
|
zone->Shutdown(true);
|
||||||
LogInfo("Shutting down...");
|
LogInfo("Shutting down...");
|
||||||
LogSys.CloseFileLogs();
|
LogSys.CloseFileLogs();
|
||||||
EQ::EventLoop::Get().Shutdown();
|
EQ::EventLoop::Get().Shutdown();
|
||||||
@@ -731,37 +731,45 @@ void UpdateWindowTitle(char *iNewTitle)
|
|||||||
|
|
||||||
bool CheckForCompatibleQuestPlugins()
|
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 lua_found = false;
|
||||||
bool perl_found = false;
|
bool perl_found = false;
|
||||||
|
|
||||||
for (const auto& directory : directories) {
|
try {
|
||||||
for (const auto& file : fs::directory_iterator(path.GetServerPath() + "/" + directory)) {
|
for (const auto &[directory, flag]: directories) {
|
||||||
if (file.is_regular_file()) {
|
std::string dir_path = path.GetServerPath() + "/" + directory;
|
||||||
auto f = file.path().string();
|
if (!File::Exists(dir_path)) { continue; }
|
||||||
if (File::Exists(f)) {
|
|
||||||
auto r = File::GetContents(std::filesystem::path{ f }.string());
|
for (const auto &file: fs::directory_iterator(dir_path)) {
|
||||||
if (Strings::Contains(r.contents, "CheckHandin")) {
|
if (!file.is_regular_file()) { continue; }
|
||||||
if (Strings::EqualFold(directory, "lua_modules")) {
|
|
||||||
|
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;
|
lua_found = true;
|
||||||
} else if (Strings::EqualFold(directory, "plugins")) {
|
}
|
||||||
|
else {
|
||||||
perl_found = true;
|
perl_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lua_found && perl_found) {
|
if (lua_found && perl_found) { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (const fs::filesystem_error &ex) {
|
||||||
|
LogError("Failed to check for compatible quest plugins: {}", ex.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lua_found) {
|
if (!lua_found) {
|
||||||
LogError("Failed to find CheckHandin in lua_modules");
|
LogError("Failed to find CheckHandin in lua_modules");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!perl_found) {
|
if (!perl_found) {
|
||||||
LogError("Failed to find CheckHandin in plugins");
|
LogError("Failed to find CheckHandin in plugins");
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-4
@@ -531,6 +531,9 @@ Mob::Mob(
|
|||||||
|
|
||||||
Mob::~Mob()
|
Mob::~Mob()
|
||||||
{
|
{
|
||||||
|
entity_list.RemoveMobFromCloseLists(this);
|
||||||
|
m_close_mobs.clear();
|
||||||
|
|
||||||
quest_manager.stopalltimers(this);
|
quest_manager.stopalltimers(this);
|
||||||
|
|
||||||
mMovementManager->RemoveMob(this);
|
mMovementManager->RemoveMob(this);
|
||||||
@@ -570,11 +573,8 @@ Mob::~Mob()
|
|||||||
entity_list.UnMarkNPC(GetID());
|
entity_list.UnMarkNPC(GetID());
|
||||||
UninitializeBuffSlots();
|
UninitializeBuffSlots();
|
||||||
|
|
||||||
entity_list.RemoveMobFromCloseLists(this);
|
|
||||||
entity_list.RemoveAuraFromMobs(this);
|
entity_list.RemoveAuraFromMobs(this);
|
||||||
|
|
||||||
m_close_mobs.clear();
|
|
||||||
|
|
||||||
ClearDataBucketCache();
|
ClearDataBucketCache();
|
||||||
|
|
||||||
LeaveHealRotationTargetPool();
|
LeaveHealRotationTargetPool();
|
||||||
@@ -1453,6 +1453,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
|||||||
ns->spawn.flymode = 0;
|
ns->spawn.flymode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsZoneController()) {
|
||||||
|
ns->spawn.invis = 255; // gm invis
|
||||||
|
}
|
||||||
|
|
||||||
if (RuleB(Character, AllowCrossClassTrainers) && ForWho) {
|
if (RuleB(Character, AllowCrossClassTrainers) && ForWho) {
|
||||||
if (ns->spawn.class_ >= Class::WarriorGM && ns->spawn.class_ <= Class::BerserkerGM) {
|
if (ns->spawn.class_ >= Class::WarriorGM && ns->spawn.class_ <= Class::BerserkerGM) {
|
||||||
int trainer_class = Class::WarriorGM + (ForWho->GetClass() - 1);
|
int trainer_class = Class::WarriorGM + (ForWho->GetClass() - 1);
|
||||||
@@ -8347,7 +8351,7 @@ int Mob::DispatchZoneControllerEvent(
|
|||||||
RuleB(Zone, UseZoneController) &&
|
RuleB(Zone, UseZoneController) &&
|
||||||
(
|
(
|
||||||
!IsNPC() ||
|
!IsNPC() ||
|
||||||
(IsNPC() && GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID)
|
(IsNPC() && !IsZoneController())
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
|
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
|
||||||
|
|||||||
@@ -671,6 +671,7 @@ public:
|
|||||||
((static_cast<float>(current_mana) / max_mana) * 100); }
|
((static_cast<float>(current_mana) / max_mana) * 100); }
|
||||||
virtual int64 CalcMaxMana();
|
virtual int64 CalcMaxMana();
|
||||||
uint32 GetNPCTypeID() const { return npctype_id; }
|
uint32 GetNPCTypeID() const { return npctype_id; }
|
||||||
|
inline bool IsZoneController() const { return npctype_id == ZONE_CONTROLLER_NPC_ID; }
|
||||||
void SetNPCTypeID(uint32 npctypeid) { npctype_id = npctypeid; }
|
void SetNPCTypeID(uint32 npctypeid) { npctype_id = npctypeid; }
|
||||||
inline const glm::vec4& GetPosition() const { return m_Position; }
|
inline const glm::vec4& GetPosition() const { return m_Position; }
|
||||||
inline void SetPosition(const float x, const float y, const float z) { m_Position.x = x; m_Position.y = y; m_Position.z = z; }
|
inline void SetPosition(const float x, const float y, const float z) { m_Position.x = x; m_Position.y = y; m_Position.z = z; }
|
||||||
|
|||||||
+62
-7
@@ -62,6 +62,7 @@
|
|||||||
#else
|
#else
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern Zone* zone;
|
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),
|
attacked_timer(CombatEventTimer_expire),
|
||||||
swarm_timer(100),
|
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),
|
classattack_timer(1000),
|
||||||
monkattack_timer(1000),
|
monkattack_timer(1000),
|
||||||
knightattack_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 (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)) {
|
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TICK)) {
|
||||||
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
||||||
}
|
}
|
||||||
@@ -895,13 +941,14 @@ bool NPC::SpawnZoneController()
|
|||||||
|
|
||||||
npc_type->findable = 0;
|
npc_type->findable = 0;
|
||||||
npc_type->trackable = 0;
|
npc_type->trackable = 0;
|
||||||
|
npc_type->untargetable = 1;
|
||||||
|
|
||||||
strcpy(npc_type->special_abilities, "12,1^13,1^14,1^15,1^16,1^17,1^19,1^22,1^24,1^25,1^28,1^31,1^35,1^39,1^42,1");
|
strcpy(npc_type->special_abilities, "1,1,3000,50^12,1^14,1^16,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^26,1^32,1^33,1^35,1^46,1^47,1^48,1^49,1^50,1^52,1^53,1^54,1^55,1^56,1^57,1");
|
||||||
|
|
||||||
glm::vec4 point;
|
glm::vec4 point;
|
||||||
point.x = 3000;
|
point.x = 30000;
|
||||||
point.y = 1000;
|
point.y = 10000;
|
||||||
point.z = 500;
|
point.z = -10000;
|
||||||
|
|
||||||
auto npc = new NPC(npc_type, nullptr, point, GravityBehavior::Flying);
|
auto npc = new NPC(npc_type, nullptr, point, GravityBehavior::Flying);
|
||||||
npc->GiveNPCTypeData(npc_type);
|
npc->GiveNPCTypeData(npc_type);
|
||||||
@@ -4269,7 +4316,7 @@ bool NPC::CanPetTakeItem(const EQ::ItemInstance *inst)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsPetOwnerClient() && !IsCharmedPet()) {
|
if (!IsPetOwnerOfClientBot() && !IsCharmedPet()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4351,6 +4398,10 @@ bool NPC::CheckHandin(
|
|||||||
h = m_hand_in;
|
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 = {};
|
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
|
// if we've already started the hand-in process, we don't want to re-process the hand-in data
|
||||||
@@ -4432,7 +4483,7 @@ bool NPC::CheckHandin(
|
|||||||
|
|
||||||
// multi-quest
|
// multi-quest
|
||||||
if (IsMultiQuestEnabled()) {
|
if (IsMultiQuestEnabled()) {
|
||||||
for (auto &h_item: h.items) {
|
for (auto &h_item: m_hand_in.items) {
|
||||||
for (const auto &r_item: r.items) {
|
for (const auto &r_item: r.items) {
|
||||||
if (h_item.item_id == r_item.item_id && h_item.count == r_item.count) {
|
if (h_item.item_id == r_item.item_id && h_item.count == r_item.count) {
|
||||||
h_item.is_multiquest_item = true;
|
h_item.is_multiquest_item = true;
|
||||||
@@ -4777,7 +4828,11 @@ NPC::Handin NPC::ReturnHandinItems(Client *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
c->PushItemOnCursor(*i.item, true);
|
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;
|
returned_handin = true;
|
||||||
return true; // Mark this item for removal
|
return true; // Mark this item for removal
|
||||||
|
|||||||
+47
@@ -601,6 +601,41 @@ public:
|
|||||||
bool HasProcessedHandinReturn() { return m_has_processed_handin_return; }
|
bool HasProcessedHandinReturn() { return m_has_processed_handin_return; }
|
||||||
bool HandinStarted() { return m_handin_started; }
|
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; }
|
||||||
|
|
||||||
|
inline void LoadBuffsFromState(std::vector<Buffs_Struct> in_buffs) {
|
||||||
|
int i = 0;
|
||||||
|
for (auto &b: in_buffs) {
|
||||||
|
buffs[i].spellid = b.spellid;
|
||||||
|
buffs[i].casterlevel = b.casterlevel;
|
||||||
|
buffs[i].casterid = b.casterid;
|
||||||
|
strncpy(buffs[i].caster_name, b.caster_name, 64);
|
||||||
|
buffs[i].ticsremaining = b.ticsremaining;
|
||||||
|
buffs[i].counters = b.counters;
|
||||||
|
buffs[i].hit_number = b.hit_number;
|
||||||
|
buffs[i].melee_rune = b.melee_rune;
|
||||||
|
buffs[i].magic_rune = b.magic_rune;
|
||||||
|
buffs[i].dot_rune = b.dot_rune;
|
||||||
|
buffs[i].caston_x = b.caston_x;
|
||||||
|
buffs[i].caston_y = b.caston_y;
|
||||||
|
buffs[i].caston_z = b.caston_z;
|
||||||
|
buffs[i].ExtraDIChance = b.ExtraDIChance;
|
||||||
|
buffs[i].RootBreakChance = b.RootBreakChance;
|
||||||
|
buffs[i].instrument_mod = b.instrument_mod;
|
||||||
|
buffs[i].virus_spread_time = b.virus_spread_time;
|
||||||
|
buffs[i].persistant_buff = b.persistant_buff;
|
||||||
|
buffs[i].client = b.client;
|
||||||
|
buffs[i].UpdateClient = b.UpdateClient;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
CalcBonuses();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void HandleRoambox();
|
void HandleRoambox();
|
||||||
@@ -622,6 +657,18 @@ protected:
|
|||||||
uint32 m_loot_platinum;
|
uint32 m_loot_platinum;
|
||||||
LootItems m_loot_items;
|
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;
|
std::list<NpcFactionEntriesRepository::NpcFactionEntries> faction_list;
|
||||||
|
|
||||||
int32 npc_faction_id;
|
int32 npc_faction_id;
|
||||||
|
|||||||
+28
-8
@@ -58,7 +58,7 @@ void Client::SendBulkParcels()
|
|||||||
p.second.aug_slot_6
|
p.second.aug_slot_6
|
||||||
));
|
));
|
||||||
if (inst) {
|
if (inst) {
|
||||||
inst->SetCharges(p.second.quantity > 0 ? p.second.quantity : 1);
|
inst->SetCharges(p.second.quantity);
|
||||||
inst->SetMerchantCount(1);
|
inst->SetMerchantCount(1);
|
||||||
inst->SetMerchantSlot(p.second.slot_id);
|
inst->SetMerchantSlot(p.second.slot_id);
|
||||||
if (inst->IsStackable()) {
|
if (inst->IsStackable()) {
|
||||||
@@ -161,7 +161,7 @@ void Client::SendParcel(Parcel_Struct &parcel_in)
|
|||||||
p.aug_slot_6
|
p.aug_slot_6
|
||||||
));
|
));
|
||||||
if (inst) {
|
if (inst) {
|
||||||
inst->SetCharges(p.quantity > 0 ? p.quantity : 1);
|
inst->SetCharges(p.quantity);
|
||||||
inst->SetMerchantCount(1);
|
inst->SetMerchantCount(1);
|
||||||
inst->SetMerchantSlot(p.slot_id);
|
inst->SetMerchantSlot(p.slot_id);
|
||||||
if (inst->IsStackable()) {
|
if (inst->IsStackable()) {
|
||||||
@@ -272,6 +272,10 @@ void Client::SendParcelStatus()
|
|||||||
|
|
||||||
void Client::DoParcelSend(const Parcel_Struct *parcel_in)
|
void Client::DoParcelSend(const Parcel_Struct *parcel_in)
|
||||||
{
|
{
|
||||||
|
if (IsCasting()) {
|
||||||
|
StopCasting();
|
||||||
|
}
|
||||||
|
|
||||||
auto send_to_client = CharacterParcelsRepository::GetParcelCountAndCharacterName(database, parcel_in->send_to);
|
auto send_to_client = CharacterParcelsRepository::GetParcelCountAndCharacterName(database, parcel_in->send_to);
|
||||||
auto merchant = entity_list.GetMob(parcel_in->npc_id);
|
auto merchant = entity_list.GetMob(parcel_in->npc_id);
|
||||||
if (!merchant) {
|
if (!merchant) {
|
||||||
@@ -382,7 +386,7 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in)
|
|||||||
quantity = parcel_in->quantity;
|
quantity = parcel_in->quantity;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
quantity = inst->GetCharges() > 0 ? inst->GetCharges() : parcel_in->quantity;
|
quantity = inst->GetCharges() >= 0 ? inst->GetCharges() : parcel_in->quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterParcelsRepository::CharacterParcels parcel_out{};
|
CharacterParcelsRepository::CharacterParcels parcel_out{};
|
||||||
@@ -434,13 +438,13 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in)
|
|||||||
cpc.aug_slot_5 = augs.at(4);
|
cpc.aug_slot_5 = augs.at(4);
|
||||||
cpc.aug_slot_6 = augs.at(5);
|
cpc.aug_slot_6 = augs.at(5);
|
||||||
}
|
}
|
||||||
cpc.quantity = kv.second->GetCharges() > 0 ? kv.second->GetCharges() : 1;
|
cpc.quantity = kv.second->GetCharges() >= 0 ? kv.second->GetCharges() : 1;
|
||||||
all_entries.push_back(cpc);
|
all_entries.push_back(cpc);
|
||||||
}
|
}
|
||||||
CharacterParcelsContainersRepository::InsertMany(database, all_entries);
|
CharacterParcelsContainersRepository::InsertMany(database, all_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveItemBySerialNumber(inst->GetSerialNumber(), parcel_out.quantity);
|
RemoveItemBySerialNumber(inst->GetSerialNumber(), parcel_out.quantity == 0 ? 1 : parcel_out.quantity);
|
||||||
std::unique_ptr<EQApplicationPacket> outapp(new EQApplicationPacket(OP_ShopSendParcel));
|
std::unique_ptr<EQApplicationPacket> outapp(new EQApplicationPacket(OP_ShopSendParcel));
|
||||||
QueuePacket(outapp.get());
|
QueuePacket(outapp.get());
|
||||||
|
|
||||||
@@ -642,9 +646,9 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
|
|||||||
if (p != m_parcels.end()) {
|
if (p != m_parcels.end()) {
|
||||||
uint32 item_id = parcel_in.parcel_item_id;
|
uint32 item_id = parcel_in.parcel_item_id;
|
||||||
uint32 item_quantity = p->second.quantity;
|
uint32 item_quantity = p->second.quantity;
|
||||||
if (!item_id || !item_quantity) {
|
if (!item_id) {
|
||||||
LogError(
|
LogError(
|
||||||
"Attempt to retrieve parcel with erroneous item id or quantity for client character id {}.",
|
"Attempt to retrieve parcel with erroneous item id for client character id {}.",
|
||||||
CharacterID()
|
CharacterID()
|
||||||
);
|
);
|
||||||
SendParcelRetrieveAck();
|
SendParcelRetrieveAck();
|
||||||
@@ -884,6 +888,22 @@ void Client::AddParcel(CharacterParcelsRepository::CharacterParcels &parcel)
|
|||||||
"Unable to send parcel at this time. Please try again later."
|
"Unable to send parcel at this time. Please try again later."
|
||||||
);
|
);
|
||||||
SendParcelAck();
|
SendParcelAck();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 Client::FindNextFreeParcelSlotUsingMemory()
|
||||||
|
{
|
||||||
|
auto const results = GetParcels();
|
||||||
|
|
||||||
|
if (results.empty()) {
|
||||||
|
return PARCEL_BEGIN_SLOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32 i = PARCEL_BEGIN_SLOT; i <= RuleI(Parcel, ParcelMaxItems); i++) {
|
||||||
|
if (!results.contains(i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return INVALID_INDEX;
|
||||||
|
}
|
||||||
+28
-10
@@ -1142,16 +1142,6 @@ void Perl_Client_SetStartZone(Client* self, uint32 zone_id, float x, float y, fl
|
|||||||
self->SetStartZone(zone_id, x, y, z, heading);
|
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
|
void Perl_Client_AddPVPPoints(Client* self, uint32 points) // @categories Currency and Points
|
||||||
{
|
{
|
||||||
self->AddPVPPoints(points);
|
self->AddPVPPoints(points);
|
||||||
@@ -3306,6 +3296,31 @@ std::string Perl_Client_GetPotionBeltItemName(Client* self, uint8 slot_id)
|
|||||||
return self->GetPotionBeltItemName(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()
|
void perl_register_client()
|
||||||
{
|
{
|
||||||
perl::interpreter perl(PERL_GET_THX);
|
perl::interpreter perl(PERL_GET_THX);
|
||||||
@@ -3630,6 +3645,9 @@ void perl_register_client()
|
|||||||
package.add("IsTaskCompleted", &Perl_Client_IsTaskCompleted);
|
package.add("IsTaskCompleted", &Perl_Client_IsTaskCompleted);
|
||||||
package.add("KeyRingAdd", &Perl_Client_KeyRingAdd);
|
package.add("KeyRingAdd", &Perl_Client_KeyRingAdd);
|
||||||
package.add("KeyRingCheck", &Perl_Client_KeyRingCheck);
|
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("Kick", &Perl_Client_Kick);
|
||||||
package.add("LearnDisciplines", &Perl_Client_LearnDisciplines);
|
package.add("LearnDisciplines", &Perl_Client_LearnDisciplines);
|
||||||
package.add("LearnRecipe", &Perl_Client_LearnRecipe);
|
package.add("LearnRecipe", &Perl_Client_LearnRecipe);
|
||||||
|
|||||||
@@ -806,6 +806,11 @@ void Perl_NPC_MultiQuestEnable(NPC* self)
|
|||||||
self->MultiQuestEnable();
|
self->MultiQuestEnable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Perl_NPC_IsResumedFromZoneSuspend(NPC* self)
|
||||||
|
{
|
||||||
|
return self->IsResumedFromZoneSuspend();
|
||||||
|
}
|
||||||
|
|
||||||
bool Perl_NPC_CheckHandin(
|
bool Perl_NPC_CheckHandin(
|
||||||
NPC* self,
|
NPC* self,
|
||||||
Client* c,
|
Client* c,
|
||||||
@@ -983,6 +988,7 @@ void perl_register_npc()
|
|||||||
package.add("IsOnHatelist", &Perl_NPC_IsOnHatelist);
|
package.add("IsOnHatelist", &Perl_NPC_IsOnHatelist);
|
||||||
package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget);
|
package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget);
|
||||||
package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn);
|
package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn);
|
||||||
|
package.add("IsResumedFromZoneSuspend", &Perl_NPC_IsResumedFromZoneSuspend);
|
||||||
package.add("IsTaunting", &Perl_NPC_IsTaunting);
|
package.add("IsTaunting", &Perl_NPC_IsTaunting);
|
||||||
package.add("IsUnderwaterOnly", (bool(*)(NPC*))&Perl_NPC_IsUnderwaterOnly);
|
package.add("IsUnderwaterOnly", (bool(*)(NPC*))&Perl_NPC_IsUnderwaterOnly);
|
||||||
package.add("MerchantCloseShop", &Perl_NPC_MerchantCloseShop);
|
package.add("MerchantCloseShop", &Perl_NPC_MerchantCloseShop);
|
||||||
|
|||||||
@@ -561,6 +561,43 @@ std::string Perl_Zone_GetBucketRemaining(Zone* self, const std::string bucket_na
|
|||||||
return self->GetBucketRemaining(bucket_name);
|
return self->GetBucketRemaining(bucket_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Perl_Zone_ClearVariables(Zone* self)
|
||||||
|
{
|
||||||
|
self->ClearVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Perl_Zone_DeleteVariable(Zone* self, const std::string variable_name)
|
||||||
|
{
|
||||||
|
return self->DeleteVariable(variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Perl_Zone_GetVariable(Zone* self, const std::string variable_name)
|
||||||
|
{
|
||||||
|
return self->GetVariable(variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
perl::array Perl_Zone_GetVariables(Zone* self)
|
||||||
|
{
|
||||||
|
perl::array a;
|
||||||
|
|
||||||
|
const auto& l = self->GetVariables();
|
||||||
|
for (const auto& v : l) {
|
||||||
|
a.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Perl_Zone_SetVariable(Zone* self, const std::string variable_name, const std::string variable_value)
|
||||||
|
{
|
||||||
|
self->SetVariable(variable_name, variable_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Perl_Zone_VariableExists(Zone* self, const std::string variable_name)
|
||||||
|
{
|
||||||
|
return self->VariableExists(variable_name);
|
||||||
|
}
|
||||||
|
|
||||||
void perl_register_zone()
|
void perl_register_zone()
|
||||||
{
|
{
|
||||||
perl::interpreter perl(PERL_GET_THX);
|
perl::interpreter perl(PERL_GET_THX);
|
||||||
@@ -573,7 +610,9 @@ void perl_register_zone()
|
|||||||
package.add("CanDoCombat", &Perl_Zone_CanDoCombat);
|
package.add("CanDoCombat", &Perl_Zone_CanDoCombat);
|
||||||
package.add("CanLevitate", &Perl_Zone_CanLevitate);
|
package.add("CanLevitate", &Perl_Zone_CanLevitate);
|
||||||
package.add("ClearSpawnTimers", &Perl_Zone_ClearSpawnTimers);
|
package.add("ClearSpawnTimers", &Perl_Zone_ClearSpawnTimers);
|
||||||
|
package.add("ClearVariables", &Perl_Zone_ClearVariables);
|
||||||
package.add("DeleteBucket", &Perl_Zone_DeleteBucket);
|
package.add("DeleteBucket", &Perl_Zone_DeleteBucket);
|
||||||
|
package.add("DeleteVariable", &Perl_Zone_DeleteVariable);
|
||||||
package.add("Depop", (void(*)(Zone*))&Perl_Zone_Depop);
|
package.add("Depop", (void(*)(Zone*))&Perl_Zone_Depop);
|
||||||
package.add("Depop", (void(*)(Zone*, bool))&Perl_Zone_Depop);
|
package.add("Depop", (void(*)(Zone*, bool))&Perl_Zone_Depop);
|
||||||
package.add("Despawn", &Perl_Zone_Despawn);
|
package.add("Despawn", &Perl_Zone_Despawn);
|
||||||
@@ -655,6 +694,8 @@ void perl_register_zone()
|
|||||||
package.add("GetZoneType", &Perl_Zone_GetZoneType);
|
package.add("GetZoneType", &Perl_Zone_GetZoneType);
|
||||||
package.add("GetUnderworld", &Perl_Zone_GetUnderworld);
|
package.add("GetUnderworld", &Perl_Zone_GetUnderworld);
|
||||||
package.add("GetUnderworldTeleportIndex", &Perl_Zone_GetUnderworldTeleportIndex);
|
package.add("GetUnderworldTeleportIndex", &Perl_Zone_GetUnderworldTeleportIndex);
|
||||||
|
package.add("GetVariable", &Perl_Zone_GetVariable);
|
||||||
|
package.add("GetVariables", &Perl_Zone_GetVariables);
|
||||||
package.add("GetWalkSpeed", &Perl_Zone_GetWalkSpeed);
|
package.add("GetWalkSpeed", &Perl_Zone_GetWalkSpeed);
|
||||||
package.add("GetZoneZType", &Perl_Zone_GetZoneZType);
|
package.add("GetZoneZType", &Perl_Zone_GetZoneZType);
|
||||||
package.add("GetZoneTotalBlockedSpells", &Perl_Zone_GetZoneTotalBlockedSpells);
|
package.add("GetZoneTotalBlockedSpells", &Perl_Zone_GetZoneTotalBlockedSpells);
|
||||||
@@ -685,7 +726,9 @@ void perl_register_zone()
|
|||||||
package.add("SetInstanceTimer", &Perl_Zone_SetInstanceTimer);
|
package.add("SetInstanceTimer", &Perl_Zone_SetInstanceTimer);
|
||||||
package.add("SetInstanceTimeRemaining", &Perl_Zone_SetInstanceTimeRemaining);
|
package.add("SetInstanceTimeRemaining", &Perl_Zone_SetInstanceTimeRemaining);
|
||||||
package.add("SetIsHotzone", &Perl_Zone_SetIsHotzone);
|
package.add("SetIsHotzone", &Perl_Zone_SetIsHotzone);
|
||||||
|
package.add("SetVariable", &Perl_Zone_SetVariable);
|
||||||
package.add("ShowZoneGlobalLoot", &Perl_Zone_ShowZoneGlobalLoot);
|
package.add("ShowZoneGlobalLoot", &Perl_Zone_ShowZoneGlobalLoot);
|
||||||
|
package.add("VariableExists", &Perl_Zone_VariableExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //EMBPERL_XS_CLASSES
|
#endif //EMBPERL_XS_CLASSES
|
||||||
|
|||||||
+7
-4
@@ -165,12 +165,15 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
|
|||||||
// 4 - Keep DB name
|
// 4 - Keep DB name
|
||||||
// 5 - `s ward
|
// 5 - `s ward
|
||||||
|
|
||||||
if (IsClient() && !petname) {
|
const auto vanity_name = (IsClient() && !petname) ? CharacterPetNameRepository::FindOne(database, CastToClient()->CharacterID()) : CharacterPetNameRepository::CharacterPetName{};
|
||||||
const auto vanity_name = CharacterPetNameRepository::FindOne(database, CastToClient()->CharacterID());
|
|
||||||
if (!vanity_name.name.empty()) {
|
if (
|
||||||
|
IsClient() &&
|
||||||
|
!petname &&
|
||||||
|
!vanity_name.name.empty()
|
||||||
|
) {
|
||||||
petname = vanity_name.name.c_str();
|
petname = vanity_name.name.c_str();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (petname != nullptr) {
|
if (petname != nullptr) {
|
||||||
// Name was provided, use it.
|
// Name was provided, use it.
|
||||||
|
|||||||
@@ -435,6 +435,12 @@ int QuestParserCollection::EventNPC(
|
|||||||
std::vector<std::any>* extra_pointers
|
std::vector<std::any>* extra_pointers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (npc->IsResumedFromZoneSuspend()) {
|
||||||
|
if (event_id == EVENT_DEATH_COMPLETE || event_id == EVENT_DEATH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const int local_return = EventNPCLocal(event_id, npc, init, data, extra_data, extra_pointers);
|
const int local_return = EventNPCLocal(event_id, npc, init, data, extra_data, extra_pointers);
|
||||||
const int global_return = EventNPCGlobal(event_id, npc, init, data, extra_data, extra_pointers);
|
const int global_return = EventNPCGlobal(event_id, npc, init, data, extra_data, extra_pointers);
|
||||||
const int default_return = DispatchEventNPC(event_id, npc, init, data, extra_data, extra_pointers);
|
const int default_return = DispatchEventNPC(event_id, npc, init, data, extra_data, extra_pointers);
|
||||||
|
|||||||
+18
-3
@@ -1623,10 +1623,10 @@ void QuestManager::setsky(uint8 new_sky) {
|
|||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuestManager::setguild(uint32 new_guild_id, uint8 new_rank) {
|
void QuestManager::SetGuild(uint32 new_guild_id, uint8 new_rank) {
|
||||||
QuestManagerCurrentQuestVars();
|
QuestManagerCurrentQuestVars();
|
||||||
if (initiator) {
|
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
|
gid
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
if (!guild_mgr.SetGuild(character_id, gid, GUILD_LEADER)) {
|
if (!guild_mgr.SetGuild(initiator, gid, GUILD_LEADER)) {
|
||||||
worldserver.SendEmoteMessage(
|
worldserver.SendEmoteMessage(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -4606,3 +4606,18 @@ void QuestManager::SpawnGrid(uint32 npc_id, glm::vec4 position, float spacing, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QuestManager::handin(std::map<std::string, uint32> required) {
|
||||||
|
QuestManagerCurrentQuestVars();
|
||||||
|
if (!owner || !initiator) {
|
||||||
|
LogQuests("QuestManager::handin called with nullptr owner. Probably syntax error in quest file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner && !owner->IsNPC()) {
|
||||||
|
LogQuests("QuestManager::handin called with non-NPC owner. Probably syntax error in quest file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return owner->CastToNPC()->CheckHandin(initiator, {}, required, {});
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -152,7 +152,7 @@ public:
|
|||||||
void faction(int faction_id, int faction_value, int temp);
|
void faction(int faction_id, int faction_value, int temp);
|
||||||
void rewardfaction(int faction_id, int faction_value);
|
void rewardfaction(int faction_id, int faction_value);
|
||||||
void setsky(uint8 new_sky);
|
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 CreateGuild(const char *guild_name, const char *leader);
|
||||||
void settime(uint8 new_hour, uint8 new_min, bool update_world = true);
|
void settime(uint8 new_hour, uint8 new_min, bool update_world = true);
|
||||||
void itemlink(int item_id);
|
void itemlink(int item_id);
|
||||||
@@ -376,6 +376,7 @@ public:
|
|||||||
bool botquest();
|
bool botquest();
|
||||||
bool createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender);
|
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:
|
private:
|
||||||
std::stack<running_quest> quests_running_;
|
std::stack<running_quest> quests_running_;
|
||||||
|
|||||||
+29
-5
@@ -16,6 +16,7 @@
|
|||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cereal/archives/json.hpp>
|
||||||
#include "../common/global_define.h"
|
#include "../common/global_define.h"
|
||||||
#include "../common/strings.h"
|
#include "../common/strings.h"
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include "../common/repositories/spawn2_repository.h"
|
#include "../common/repositories/spawn2_repository.h"
|
||||||
#include "../common/repositories/spawn2_disabled_repository.h"
|
#include "../common/repositories/spawn2_disabled_repository.h"
|
||||||
#include "../common/repositories/respawn_times_repository.h"
|
#include "../common/repositories/respawn_times_repository.h"
|
||||||
|
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||||
|
|
||||||
extern EntityList entity_list;
|
extern EntityList entity_list;
|
||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
@@ -86,7 +88,7 @@ Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id,
|
|||||||
y = in_y;
|
y = in_y;
|
||||||
z = in_z;
|
z = in_z;
|
||||||
heading = in_heading;
|
heading = in_heading;
|
||||||
respawn_ = respawn;
|
m_respawn_time = respawn;
|
||||||
variance_ = variance;
|
variance_ = variance;
|
||||||
grid_ = grid;
|
grid_ = grid;
|
||||||
path_when_zone_idle = in_path_when_zone_idle;
|
path_when_zone_idle = in_path_when_zone_idle;
|
||||||
@@ -95,6 +97,7 @@ Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id,
|
|||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
enabled = in_enabled;
|
enabled = in_enabled;
|
||||||
this->anim = anim;
|
this->anim = anim;
|
||||||
|
currentnpcid = 0;
|
||||||
|
|
||||||
if(timeleft == 0xFFFFFFFF) {
|
if(timeleft == 0xFFFFFFFF) {
|
||||||
//special disable timeleft
|
//special disable timeleft
|
||||||
@@ -115,7 +118,7 @@ Spawn2::~Spawn2()
|
|||||||
|
|
||||||
uint32 Spawn2::resetTimer()
|
uint32 Spawn2::resetTimer()
|
||||||
{
|
{
|
||||||
uint32 rspawn = respawn_ * 1000;
|
uint32 rspawn = m_respawn_time * 1000;
|
||||||
|
|
||||||
if (variance_ != 0) {
|
if (variance_ != 0) {
|
||||||
int var_over_2 = (variance_ * 1000) / 2;
|
int var_over_2 = (variance_ * 1000) / 2;
|
||||||
@@ -150,12 +153,12 @@ uint32 Spawn2::despawnTimer(uint32 despawn_timer)
|
|||||||
bool Spawn2::Process() {
|
bool Spawn2::Process() {
|
||||||
IsDespawned = false;
|
IsDespawned = false;
|
||||||
|
|
||||||
if (!Enabled())
|
if (!Enabled()) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//grab our spawn group
|
//grab our spawn group
|
||||||
SpawnGroup *spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
|
SpawnGroup *spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
|
||||||
|
|
||||||
if (NPCPointerValid() && (spawn_group && spawn_group->despawn == 0 || condition_id != 0)) {
|
if (NPCPointerValid() && (spawn_group && spawn_group->despawn == 0 || condition_id != 0)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -195,7 +198,13 @@ bool Spawn2::Process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//have the spawn group pick an NPC for us
|
//have the spawn group pick an NPC for us
|
||||||
uint32 npcid = spawn_group->GetNPCType(condition_value);
|
uint32 npcid = 0;
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown) && currentnpcid && currentnpcid > 0) {
|
||||||
|
npcid = currentnpcid;
|
||||||
|
} else {
|
||||||
|
npcid = spawn_group->GetNPCType(condition_value);
|
||||||
|
}
|
||||||
|
|
||||||
if (npcid == 0) {
|
if (npcid == 0) {
|
||||||
LogSpawns("Spawn2 [{}]: Spawn group [{}] did not yeild an NPC! not spawning", spawn2_id, spawngroup_id_);
|
LogSpawns("Spawn2 [{}]: Spawn group [{}] did not yeild an NPC! not spawning", spawn2_id, spawngroup_id_);
|
||||||
|
|
||||||
@@ -267,10 +276,12 @@ bool Spawn2::Process() {
|
|||||||
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water);
|
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water);
|
||||||
|
|
||||||
npcthis = npc;
|
npcthis = npc;
|
||||||
|
|
||||||
npc->AddLootTable();
|
npc->AddLootTable();
|
||||||
if (npc->DropsGlobalLoot()) {
|
if (npc->DropsGlobalLoot()) {
|
||||||
npc->CheckGlobalLootTables();
|
npc->CheckGlobalLootTables();
|
||||||
}
|
}
|
||||||
|
|
||||||
npc->SetSpawnGroupId(spawngroup_id_);
|
npc->SetSpawnGroupId(spawngroup_id_);
|
||||||
npc->SaveGuardPointAnim(anim);
|
npc->SaveGuardPointAnim(anim);
|
||||||
npc->SetAppearance((EmuAppearance) anim);
|
npc->SetAppearance((EmuAppearance) anim);
|
||||||
@@ -351,6 +362,7 @@ void Spawn2::LoadGrid(int start_wp) {
|
|||||||
void Spawn2::Reset() {
|
void Spawn2::Reset() {
|
||||||
timer.Start(resetTimer());
|
timer.Start(resetTimer());
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
LogSpawns("Spawn2 [{}]: Spawn reset, repop in [{}] ms", spawn2_id, timer.GetRemainingTime());
|
LogSpawns("Spawn2 [{}]: Spawn reset, repop in [{}] ms", spawn2_id, timer.GetRemainingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +370,7 @@ void Spawn2::Depop() {
|
|||||||
timer.Disable();
|
timer.Disable();
|
||||||
LogSpawns("Spawn2 [{}]: Spawn reset, repop disabled", spawn2_id);
|
LogSpawns("Spawn2 [{}]: Spawn reset, repop disabled", spawn2_id);
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spawn2::Repop(uint32 delay) {
|
void Spawn2::Repop(uint32 delay) {
|
||||||
@@ -369,6 +382,7 @@ void Spawn2::Repop(uint32 delay) {
|
|||||||
timer.Start(delay);
|
timer.Start(delay);
|
||||||
}
|
}
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spawn2::ForceDespawn()
|
void Spawn2::ForceDespawn()
|
||||||
@@ -387,12 +401,14 @@ void Spawn2::ForceDespawn()
|
|||||||
npcthis->Depop(true);
|
npcthis->Depop(true);
|
||||||
IsDespawned = true;
|
IsDespawned = true;
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
npcthis->Depop(false);
|
npcthis->Depop(false);
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,6 +440,7 @@ void Spawn2::DeathReset(bool realdeath)
|
|||||||
|
|
||||||
//zero out our NPC since he is now gone
|
//zero out our NPC since he is now gone
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
|
|
||||||
if(realdeath) { killcount++; }
|
if(realdeath) { killcount++; }
|
||||||
|
|
||||||
@@ -500,6 +517,12 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spa
|
|||||||
|
|
||||||
NPC::SpawnZoneController();
|
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) {
|
for (auto &s: spawns) {
|
||||||
uint32 spawn_time_left = 0;
|
uint32 spawn_time_left = 0;
|
||||||
if (spawn_times.count(s.id) != 0) {
|
if (spawn_times.count(s.id) != 0) {
|
||||||
@@ -632,6 +655,7 @@ void Spawn2::SpawnConditionChanged(const SpawnCondition &c, int16 old_value) {
|
|||||||
LogSpawns("Spawn2 [{}]: Our npcthis is currently not null. The zone thinks it is [{}]. Forcing a depop", spawn2_id, npcthis->GetName());
|
LogSpawns("Spawn2 [{}]: Our npcthis is currently not null. The zone thinks it is [{}]. Forcing a depop", spawn2_id, npcthis->GetName());
|
||||||
npcthis->Depop(false); //remove the current mob
|
npcthis->Depop(false); //remove the current mob
|
||||||
npcthis = nullptr;
|
npcthis = nullptr;
|
||||||
|
currentnpcid = 0;
|
||||||
}
|
}
|
||||||
if(new_state) { // only get repawn timer remaining when the SpawnCondition is enabled.
|
if(new_state) { // only get repawn timer remaining when the SpawnCondition is enabled.
|
||||||
timer_remaining = database.GetSpawnTimeLeft(spawn2_id,zone->GetInstanceID());
|
timer_remaining = database.GetSpawnTimeLeft(spawn2_id,zone->GetInstanceID());
|
||||||
|
|||||||
+9
-3
@@ -55,10 +55,10 @@ public:
|
|||||||
float GetZ() { return z; }
|
float GetZ() { return z; }
|
||||||
float GetHeading() { return heading; }
|
float GetHeading() { return heading; }
|
||||||
bool PathWhenZoneIdle() { return path_when_zone_idle; }
|
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; }
|
void SetVariance(uint32 newvariance) { variance_ = newvariance; }
|
||||||
const uint32 GetVariance() const { return variance_; }
|
const uint32 GetVariance() const { return variance_; }
|
||||||
uint32 RespawnTimer() { return respawn_; }
|
uint32 RespawnTimer() { return m_respawn_time; }
|
||||||
uint32 SpawnGroupID() { return spawngroup_id_; }
|
uint32 SpawnGroupID() { return spawngroup_id_; }
|
||||||
uint32 CurrentNPCID() { return currentnpcid; }
|
uint32 CurrentNPCID() { return currentnpcid; }
|
||||||
void SetCurrentNPCID(uint32 nid) { currentnpcid = nid; }
|
void SetCurrentNPCID(uint32 nid) { currentnpcid = nid; }
|
||||||
@@ -70,12 +70,18 @@ public:
|
|||||||
Timer GetTimer() { return timer; }
|
Timer GetTimer() { return timer; }
|
||||||
void SetTimer(uint32 duration) { timer.Start(duration); }
|
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:
|
protected:
|
||||||
friend class Zone;
|
friend class Zone;
|
||||||
Timer timer;
|
Timer timer;
|
||||||
private:
|
private:
|
||||||
uint32 spawn2_id;
|
uint32 spawn2_id;
|
||||||
uint32 respawn_;
|
uint32 m_respawn_time;
|
||||||
uint32 resetTimer();
|
uint32 resetTimer();
|
||||||
uint32 despawnTimer(uint32 despawn_timer);
|
uint32 despawnTimer(uint32 despawn_timer);
|
||||||
|
|
||||||
|
|||||||
@@ -1243,6 +1243,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
case SE_SummonItemIntoBag:
|
case SE_SummonItemIntoBag:
|
||||||
{
|
{
|
||||||
const EQ::ItemData *item = database.GetItem(spell.base_value[i]);
|
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
|
#ifdef SPELL_EFFECT_SPAM
|
||||||
const char *itemname = item ? item->Name : "*Unknown Item*";
|
const char *itemname = item ? item->Name : "*Unknown Item*";
|
||||||
snprintf(effect_desc, _EDLEN, "Summon Item In Bag: %s (id %d)", itemname, spell.base_value[i]);
|
snprintf(effect_desc, _EDLEN, "Summon Item In Bag: %s (id %d)", itemname, spell.base_value[i]);
|
||||||
|
|||||||
@@ -3135,6 +3135,17 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& always_stack_spells = RuleS(Spells, AlwaysStackSpells);
|
||||||
|
if (spellid1 != spellid2 && !always_stack_spells.empty()) {
|
||||||
|
const auto& v = Strings::Split(always_stack_spells, ",");
|
||||||
|
if (Strings::Contains(v, std::to_string(spellid1))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (Strings::Contains(v, std::to_string(spellid2))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
One of these is a bard song and one isn't and they're both beneficial so they should stack.
|
One of these is a bard song and one isn't and they're both beneficial so they should stack.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user