mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-22 16:28:28 +00:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df746dd07d | |||
| cd158aabca | |||
| 95687b57e9 | |||
| 77de74a089 | |||
| 1da7901029 | |||
| 738a31df51 | |||
| 62ac015fff | |||
| 4977a7c2e0 | |||
| 9967384ab8 | |||
| d3da2e5501 | |||
| 33f5c4c6a7 | |||
| e4aa6a6957 | |||
| e4d812f4b4 | |||
| bcedfe7032 | |||
| c1df3fbcb0 | |||
| 011e1d05e7 | |||
| 3f0f95976c | |||
| 77de9619b5 | |||
| 20d3ab2ac5 | |||
| 0ea47fadee | |||
| 1ce51ca3b0 | |||
| 25ef3d2cdb | |||
| 95249889a6 | |||
| 428cccfa50 | |||
| 41dd8a5754 | |||
| d02d766563 | |||
| dfd2729b28 | |||
| b92eafd21b | |||
| d6d5d992cb | |||
| d524cb6a5a | |||
| e6469878ce | |||
| 9583099ace | |||
| cf3483b402 | |||
| 311af7bbe9 | |||
| be42b73f5c | |||
| f76c798910 | |||
| ae198ae043 | |||
| 520943ebf1 | |||
| 9ac306fe67 | |||
| 7a1d69d0d4 | |||
| c873fe5a22 | |||
| e06b0c4b0c | |||
| ed2130f649 | |||
| 448a33a60c | |||
| 8f86cb353e | |||
| 178129443f | |||
| a7c3b41afc | |||
| a5a568d548 | |||
| e3198edb86 | |||
| 8568cf7d49 | |||
| 1fb7a860a1 | |||
| 7eaee2649e | |||
| a17f467b98 | |||
| 3359839a9b | |||
| 7e51e629f9 | |||
| dc6c28a52d | |||
| 78aee0780a | |||
| bcd943a964 | |||
| 56608e84bd | |||
| 8d23e710ce | |||
| 4d11077b21 | |||
| 5c0bdfdc4c | |||
| 6130e10831 | |||
| c3e1c531d2 | |||
| b52719a535 | |||
| 1af252466f | |||
| 699d22fc28 | |||
| 5d1fe68906 | |||
| 52dcf35425 | |||
| a7550fbd9e | |||
| cc0171dfe1 | |||
| 913c5da70f | |||
| 40fecbfaf5 | |||
| b1646381b0 | |||
| bb1578796b | |||
| 0e5a38f072 | |||
| 39876ab858 | |||
| ff16a76481 | |||
| ffd68eb63d | |||
| 76c1da1aad | |||
| a91e03fa43 | |||
| 453106439f | |||
| 3da24fffa4 | |||
| 8d8ef6d480 | |||
| 1f9c4b3a22 | |||
| 7dfda95d86 | |||
| 40738b29e3 | |||
| 080865faa2 | |||
| e2b545991a | |||
| b7f8d0f179 | |||
| e3588781aa | |||
| e9b84f4d11 | |||
| 4f03970fd1 | |||
| 4979da6932 | |||
| 9987029791 | |||
| eece0a92e3 | |||
| 057f96796a | |||
| f475cecdb1 | |||
| 6296ed6d41 | |||
| ac0f729aa2 | |||
| 2937852cf9 | |||
| 2cf5bae571 | |||
| 2feb05be18 | |||
| 421767e1e5 | |||
| 6e9ff52dce | |||
| aa700f8960 | |||
| 2ef959c5ed | |||
| e49ab924cc | |||
| fc3c691588 | |||
| d465a3deba | |||
| 40c9c8044b | |||
| 70a96ea098 | |||
| d5cbec714e | |||
| 6903205484 | |||
| 4c81321847 | |||
| e5cea73e0c | |||
| 23308192b5 | |||
| 29fdf7e2ae | |||
| 098498dedd | |||
| b6fb8daae8 | |||
| 563f7d5564 | |||
| 1e5abc456b | |||
| 3b0fa015a7 | |||
| c73a1e8bea | |||
| 3bfdc0cf71 | |||
| a23ac4628f | |||
| 5ef4612249 | |||
| 17f66c5d60 | |||
| 51eb95ed31 | |||
| 080abaede1 | |||
| 97e332819d | |||
| 1e41c5517e | |||
| c7a88af11a | |||
| d8ddd0aab9 | |||
| 95cbadade5 | |||
| a85f4fb703 | |||
| e63f34638b | |||
| 7918fed81c | |||
| ac24c9bf5a | |||
| 7b914c731b | |||
| 7362c0ebb5 | |||
| ae213a4e4b | |||
| 187288f3aa | |||
| abc8c3d886 | |||
| 0b2493beb8 | |||
| 9cebba5911 | |||
| 4478328b2a | |||
| 55a7e1646d | |||
| b6b8491060 | |||
| 850053a136 | |||
| 1aa8758b0a | |||
| b1aa087b9f | |||
| 1e57a0372f | |||
| 9614ea59ec | |||
| 7a648cce16 | |||
| 8640776a21 |
+304
@@ -1,3 +1,307 @@
|
||||
## [22.59.1] 11/13/2024
|
||||
|
||||
### Hotfix
|
||||
|
||||
* Fix faulty database migration condition with databuckets (9285)
|
||||
|
||||
## [22.59.0] 11/13/2024
|
||||
|
||||
### Databuckets
|
||||
|
||||
* Add database index to data_buckets ([#4535](https://github.com/EQEmu/Server/pull/4535)) @Akkadius 2024-11-09
|
||||
|
||||
### Fixes
|
||||
|
||||
* Bazaar two edge case issues resolved ([#4533](https://github.com/EQEmu/Server/pull/4533)) @neckkola 2024-11-09
|
||||
* Check if the mob is already in the close mobs list before inserting @Akkadius 2024-11-11
|
||||
* ScanCloseMobs - Ensure scanning mob has an entity ID @Akkadius 2024-11-10
|
||||
|
||||
### Performance
|
||||
|
||||
* Improvements to ScanCloseMobs logic ([#4534](https://github.com/EQEmu/Server/pull/4534)) @Akkadius 2024-11-08
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Native Database Querying Interface ([#4531](https://github.com/EQEmu/Server/pull/4531)) @hgtw 2024-11-13
|
||||
|
||||
### Rules
|
||||
|
||||
* Add Rule for restricting client versions to world server ([#4527](https://github.com/EQEmu/Server/pull/4527)) @knervous 2024-11-12
|
||||
|
||||
## [22.58.0] 11/5/2024
|
||||
|
||||
### Code
|
||||
|
||||
* Add mysql prepared statement support ([#4530](https://github.com/EQEmu/Server/pull/4530)) @hgtw 2024-11-06
|
||||
* Update perlbind to 1.1.0 ([#4529](https://github.com/EQEmu/Server/pull/4529)) @hgtw 2024-11-06
|
||||
|
||||
### Feature
|
||||
|
||||
* Focus Skill Attack Spells ([#4528](https://github.com/EQEmu/Server/pull/4528)) @mmcgarvey 2024-10-31
|
||||
|
||||
### Fixes
|
||||
|
||||
* Add Missing Lua Registers ([#4525](https://github.com/EQEmu/Server/pull/4525)) @Kinglykrab 2024-10-24
|
||||
* Fix cross_zone_set_entity_variable_by_char_id in Lua ([#4526](https://github.com/EQEmu/Server/pull/4526)) @Kinglykrab 2024-10-24
|
||||
|
||||
### Loginserver
|
||||
|
||||
* Automatifc Opcode File Creation ([#4521](https://github.com/EQEmu/Server/pull/4521)) @KimLS 2024-10-22
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Spawn Circle/Grid Methods to Perl/Lua ([#4524](https://github.com/EQEmu/Server/pull/4524)) @Kinglykrab 2024-10-24
|
||||
|
||||
## [22.57.1] 10/22/2024
|
||||
|
||||
### Bots
|
||||
|
||||
* Enable Bot Commands Only if Rule Enabled ([#4519](https://github.com/EQEmu/Server/pull/4519)) @Kinglykrab 2024-10-22
|
||||
* Fix pet buffs from saving duplicates every save ([#4520](https://github.com/EQEmu/Server/pull/4520)) @nytmyr 2024-10-22
|
||||
|
||||
### Loginserver
|
||||
|
||||
* Automatic Opcode File Creation ([#4521](https://github.com/EQEmu/Server/pull/4521)) @KimLS 2024-10-22
|
||||
|
||||
## [22.57.0] 10/20/2024
|
||||
|
||||
### Bots
|
||||
|
||||
* Add "silent" option to ^spawn and mute raid spawn ([#4494](https://github.com/EQEmu/Server/pull/4494)) @nytmyr 2024-10-05
|
||||
* Add attack flag when told to attack ([#4490](https://github.com/EQEmu/Server/pull/4490)) @nytmyr 2024-09-29
|
||||
* Fix timers loading on spawn and zone ([#4516](https://github.com/EQEmu/Server/pull/4516)) @nytmyr 2024-10-20
|
||||
|
||||
### Code
|
||||
|
||||
* Fixed a typo in Zoning.cpp ([#4515](https://github.com/EQEmu/Server/pull/4515)) @carolus21rex 2024-10-20
|
||||
* Optimization Code Cleanup ([#4489](https://github.com/EQEmu/Server/pull/4489)) @Akkadius 2024-09-30
|
||||
* Remove Extra Skill in EQ::skills::GetExtraDamageSkills() ([#4486](https://github.com/EQEmu/Server/pull/4486)) @Kinglykrab 2024-10-03
|
||||
|
||||
### Crash
|
||||
|
||||
* Fixes a crash when the faction_list db table is empty. ([#4511](https://github.com/EQEmu/Server/pull/4511)) @KimLS 2024-10-14
|
||||
|
||||
### Fixes
|
||||
|
||||
* Add character_instance_safereturns to tables_to_zero_id ([#4485](https://github.com/EQEmu/Server/pull/4485)) @Morzain 2024-09-26
|
||||
* Correctly limit max targets of PBAOE ([#4507](https://github.com/EQEmu/Server/pull/4507)) @catapultam-habeo 2024-10-11
|
||||
* FindBestZ selecting false zone floor as bestz - Results in roambox failures ([#4504](https://github.com/EQEmu/Server/pull/4504)) @fryguy503 2024-10-13
|
||||
* Fix #set motd Crash ([#4495](https://github.com/EQEmu/Server/pull/4495)) @Kinglykrab 2024-10-05
|
||||
* Fix `character_exp_modifiers` Default Values ([#4502](https://github.com/EQEmu/Server/pull/4502)) @Kinglykrab 2024-10-09
|
||||
* Fix a display error regarding a few trader/buyer query errors ([#4514](https://github.com/EQEmu/Server/pull/4514)) @neckkola 2024-10-17
|
||||
* Fix Group ID 0 in Group::SaveGroupLeaderAA() ([#4487](https://github.com/EQEmu/Server/pull/4487)) @Kinglykrab 2024-10-03
|
||||
* Fix Mercenary Encounter Crash ([#4509](https://github.com/EQEmu/Server/pull/4509)) @Kinglykrab 2024-10-12
|
||||
* Fix NPC::CanTalk() Crash ([#4499](https://github.com/EQEmu/Server/pull/4499)) @Kinglykrab 2024-10-07
|
||||
* Fix Spells:DefaultAOEMaxTargets Default Value ([#4508](https://github.com/EQEmu/Server/pull/4508)) @Kinglykrab 2024-10-12
|
||||
* Fix Targeted AOE Max Targets Rule ([#4488](https://github.com/EQEmu/Server/pull/4488)) @Kinglykrab 2024-10-03
|
||||
* fixed a bug where it would use npc value instead of faction value in the database. ([#4491](https://github.com/EQEmu/Server/pull/4491)) @regneq 2024-09-29
|
||||
* Master of Disguise should apply to illusions casted by others. ([#4506](https://github.com/EQEmu/Server/pull/4506)) @fryguy503 2024-10-11
|
||||
* Spells - Self Only (Yellow) cast when non group member is targeted ([#4503](https://github.com/EQEmu/Server/pull/4503)) @fryguy503 2024-10-11
|
||||
|
||||
### Loginserver
|
||||
|
||||
* Larion loginserver support ([#4492](https://github.com/EQEmu/Server/pull/4492)) @KimLS 2024-10-03
|
||||
* Login Fatal Error Spamming ([#4476](https://github.com/EQEmu/Server/pull/4476)) @KimLS 2024-10-09
|
||||
|
||||
### Logs
|
||||
|
||||
* Add NPC Trades to Player Events ([#4505](https://github.com/EQEmu/Server/pull/4505)) @Kinglykrab 2024-10-13
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Buff Fade Methods to Perl/Lua ([#4501](https://github.com/EQEmu/Server/pull/4501)) @Kinglykrab 2024-10-09
|
||||
* Add EVENT_READ_ITEM to Perl/Lua ([#4497](https://github.com/EQEmu/Server/pull/4497)) @Kinglykrab 2024-10-08
|
||||
* Add NPC List Filter Methods to Perl/Lua ([#4493](https://github.com/EQEmu/Server/pull/4493)) @Kinglykrab 2024-10-04
|
||||
* Add Scripting Support to Mercenaries ([#4500](https://github.com/EQEmu/Server/pull/4500)) @Kinglykrab 2024-10-11
|
||||
|
||||
### Rules
|
||||
|
||||
* Add Rule to disable PVP Regions ([#4513](https://github.com/EQEmu/Server/pull/4513)) @Kinglykrab 2024-10-17
|
||||
|
||||
## [22.56.3] 9/23/2024
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix issue with Client::SaveDisciplines() not specifying character ID ([#4481](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-23
|
||||
|
||||
## [22.56.2] 9/20/2024
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix Issue with Database::ReserveName ([#4477](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-20
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add GrantAllAAPoints() Overload To Perl/Lua ([#4474](https://github.com/EQEmu/Server/pull/4474)) @Kinglykrab 2024-09-20
|
||||
|
||||
## [22.56.1] 9/20/2024
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix Untrained Disciplines in Client::SaveDisciplines() ([#4472](https://github.com/EQEmu/Server/pull/4472)) @Kinglykrab 2024-09-13
|
||||
* Fix Infinite Loop in Adventure::Finished() ([#4473](https://github.com/EQEmu/Server/pull/4473)) @oddx2k 2024-09-13
|
||||
|
||||
## [22.56.0] 9/12/2024
|
||||
|
||||
### Code
|
||||
|
||||
* Add IsCloseToBanker method ([#4462](https://github.com/EQEmu/Server/pull/4462)) @Akkadius 2024-08-27
|
||||
|
||||
### Feature
|
||||
|
||||
* Add Rule to Limit Task Update Messages ([#4459](https://github.com/EQEmu/Server/pull/4459)) @Kinglykrab 2024-08-28
|
||||
* Allow NPCs to cast Sacrifice ([#4470](https://github.com/EQEmu/Server/pull/4470)) @fuzzlecutter 2024-09-12
|
||||
* Lazy Load Bank Contents ([#4453](https://github.com/EQEmu/Server/pull/4453)) @catapultam-habeo 2024-08-27
|
||||
|
||||
### Fixes
|
||||
|
||||
* Add RULE_STRING to RuleManager::ResetRules ([#4467](https://github.com/EQEmu/Server/pull/4467)) @Kinglykrab 2024-09-07
|
||||
* Fix Bard Effect in Migration 9237 ([#4468](https://github.com/EQEmu/Server/pull/4468)) @Kinglykrab 2024-09-09
|
||||
* ModernAAScalingEnabled() Calculation Error ([#4469](https://github.com/EQEmu/Server/pull/4469)) @carolus21rex 2024-09-11
|
||||
|
||||
### Performance
|
||||
|
||||
* Move Discipline Loading to Client::CompleteConnect() ([#4466](https://github.com/EQEmu/Server/pull/4466)) @Kinglykrab 2024-09-09
|
||||
|
||||
### Rules
|
||||
|
||||
* Add a Bandolier Swap Delay Rule ([#4465](https://github.com/EQEmu/Server/pull/4465)) @Kinglykrab 2024-09-08
|
||||
|
||||
## [22.55.1] 8/26/2024
|
||||
|
||||
### Code
|
||||
|
||||
* Remove unused methods ([#4449](https://github.com/EQEmu/Server/pull/4449)) @Kinglykrab 2024-08-22
|
||||
|
||||
### Feature
|
||||
|
||||
* Add Character:DefaultGuildRank Rule ([#4438](https://github.com/EQEmu/Server/pull/4438)) @Kinglykrab 2024-08-04
|
||||
* Add Optional Return to EVENT_DAMAGE_TAKEN ([#4454](https://github.com/EQEmu/Server/pull/4454)) @Kinglykrab 2024-08-27
|
||||
* Extend Spell Buckets Functionality ([#4441](https://github.com/EQEmu/Server/pull/4441)) @Kinglykrab 2024-08-22
|
||||
|
||||
### Fixes
|
||||
|
||||
* Apply Race & Class restrictions to Auto-Combines ([#4452](https://github.com/EQEmu/Server/pull/4452)) @catapultam-habeo 2024-08-20
|
||||
* Attune Augments when Equipped ([#4446](https://github.com/EQEmu/Server/pull/4446)) @fryguy503 2024-08-10
|
||||
* Correct missed maxlevel reference in exp.cpp ([#4463](https://github.com/EQEmu/Server/pull/4463)) @N0ctrnl 2024-08-27
|
||||
* Ensure close of Tribute Item search ([#4439](https://github.com/EQEmu/Server/pull/4439)) @joligario 2024-08-04
|
||||
* Fix AddCrystals() in Perl/Lua ([#4445](https://github.com/EQEmu/Server/pull/4445)) @Kinglykrab 2024-08-10
|
||||
* Fix Bot Spell Entries IDs Capping at 32,767 ([#4444](https://github.com/EQEmu/Server/pull/4444)) @Kinglykrab 2024-08-27
|
||||
* Fix Character ID of 0 being inserted into character_stats_record ([#4458](https://github.com/EQEmu/Server/pull/4458)) @Kinglykrab 2024-08-22
|
||||
* Fix Issue with Removed #setfaction Command ([#4448](https://github.com/EQEmu/Server/pull/4448)) @Kinglykrab 2024-08-11
|
||||
* Fix Lua Client FilteredMessage ([#4437](https://github.com/EQEmu/Server/pull/4437)) @Kinglykrab 2024-07-31
|
||||
* Fix client hotbar exchanging items when zoning ([#4460](https://github.com/EQEmu/Server/pull/4460)) @neckkola 2024-08-27
|
||||
* Fix issue with killed mob coordinates ([#4457](https://github.com/EQEmu/Server/pull/4457)) @Kinglykrab 2024-08-22
|
||||
* Imitate Death should also clear zone feign aggro ([#4436](https://github.com/EQEmu/Server/pull/4436)) @fryguy503 2024-07-31
|
||||
* client_max_level allow leveling to end of level ([#4455](https://github.com/EQEmu/Server/pull/4455)) @fryguy503 2024-08-20
|
||||
|
||||
### Improvement
|
||||
|
||||
* Filtered Messages Extension ([#4435](https://github.com/EQEmu/Server/pull/4435)) @fryguy503 2024-07-31
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add AreTasksCompleted() to Perl/Lua. ([#4456](https://github.com/EQEmu/Server/pull/4456)) @Kinglykrab 2024-08-23
|
||||
* Add Area-Based Quest Methods to Perl/Lua ([#4447](https://github.com/EQEmu/Server/pull/4447)) @Kinglykrab 2024-08-27
|
||||
* Add Several Door Methods to Perl/Lua ([#4451](https://github.com/EQEmu/Server/pull/4451)) @Kinglykrab 2024-08-16
|
||||
|
||||
### World
|
||||
|
||||
* Fix slow world bootup bug ([#4461](https://github.com/EQEmu/Server/pull/4461)) @Akkadius 2024-08-27
|
||||
|
||||
## [22.54.0] 7/30/2024
|
||||
|
||||
### Code
|
||||
|
||||
* Cleanup Client File Exporting ([#4348](https://github.com/EQEmu/Server/pull/4348)) @Kinglykrab 2024-07-31
|
||||
* Cleanup Stance Code ([#4368](https://github.com/EQEmu/Server/pull/4368)) @Kinglykrab 2024-07-03
|
||||
* Mask GM Show Buff message behind EntityVariable ([#4419](https://github.com/EQEmu/Server/pull/4419)) @nytmyr 2024-07-22
|
||||
|
||||
### Commands
|
||||
|
||||
* Extend #devtools Functionality ([#4425](https://github.com/EQEmu/Server/pull/4425)) @Kinglykrab 2024-07-23
|
||||
|
||||
### Databuckets
|
||||
|
||||
* Remove memory reserve from bulk load ([#4427](https://github.com/EQEmu/Server/pull/4427)) @Akkadius 2024-07-23
|
||||
|
||||
### Feature
|
||||
|
||||
* Add Barter/Buyer Features ([#4405](https://github.com/EQEmu/Server/pull/4405)) @neckkola 2024-07-30
|
||||
* Add Parcel notification for online players when using the Quest API ([#4418](https://github.com/EQEmu/Server/pull/4418)) @neckkola 2024-07-22
|
||||
* Implement Move Multiple Items ([#4259](https://github.com/EQEmu/Server/pull/4259)) @catapultam-habeo 2024-07-30
|
||||
|
||||
### Fixes
|
||||
|
||||
* Aegolism Spell line stacking ([#4399](https://github.com/EQEmu/Server/pull/4399)) @KayenEQ 2024-07-07
|
||||
* AllowRaidTargetBlind logic backwards ([#4400](https://github.com/EQEmu/Server/pull/4400)) @fryguy503 2024-07-01
|
||||
* AutoSplit unknown bug and cleanup. ([#4401](https://github.com/EQEmu/Server/pull/4401)) @fryguy503 2024-07-07
|
||||
* Corpse Call removing Resurrection Effects ([#4410](https://github.com/EQEmu/Server/pull/4410)) @fryguy503 2024-07-22
|
||||
* Fix #parcels add subcommand ([#4431](https://github.com/EQEmu/Server/pull/4431)) @neckkola 2024-07-29
|
||||
* Fix #setlevel Allowing Skills Above Max ([#4423](https://github.com/EQEmu/Server/pull/4423)) @Kinglykrab 2024-07-23
|
||||
* Fix Bot::SetBotStance ([#4426](https://github.com/EQEmu/Server/pull/4426)) @Kinglykrab 2024-07-23
|
||||
* Fix Client::RemoveTitle ([#4421](https://github.com/EQEmu/Server/pull/4421)) @Kinglykrab 2024-07-23
|
||||
* Fix EVENT_USE_SKILL with Sense Heading ([#4424](https://github.com/EQEmu/Server/pull/4424)) @Kinglykrab 2024-07-23
|
||||
* Fix for random disconnects when a large number of guild members zone or disconnect ([#4402](https://github.com/EQEmu/Server/pull/4402)) @neckkola 2024-07-10
|
||||
* Fix issue with quest::echo and quest::me ([#4433](https://github.com/EQEmu/Server/pull/4433)) @Kinglykrab 2024-07-30
|
||||
* Personal tributes for bard items were not applying correctly ([#4416](https://github.com/EQEmu/Server/pull/4416)) @neckkola 2024-07-16
|
||||
* Potential fix for some undesired ranged explotative behavior. ([#4413](https://github.com/EQEmu/Server/pull/4413)) @fryguy503 2024-07-22
|
||||
* Proximity Aggro for Frustrated and Undead ([#4411](https://github.com/EQEmu/Server/pull/4411)) @fryguy503 2024-07-22
|
||||
* Slay Adjustments ([#4389](https://github.com/EQEmu/Server/pull/4389)) @fryguy503 2024-07-07
|
||||
* Stop DOSing ourselves with OP_WearChange ([#4432](https://github.com/EQEmu/Server/pull/4432)) @catapultam-habeo 2024-07-30
|
||||
* [Quest API] Fix getraididbycharid and getgroupidbycharid ([#4417](https://github.com/EQEmu/Server/pull/4417)) @nytmyr 2024-07-16
|
||||
|
||||
### Improvement
|
||||
|
||||
* Flee Overhaul ([#4407](https://github.com/EQEmu/Server/pull/4407)) @fryguy503 2024-07-30
|
||||
|
||||
### Rules
|
||||
|
||||
* Add HasteCap and Hastev3Cap rules for NPCs, Bots and Mercs ([#4406](https://github.com/EQEmu/Server/pull/4406)) @nytmyr 2024-07-22
|
||||
|
||||
### Zone Instances
|
||||
|
||||
* Revert " Handle routing to instances when using evac/succor " (#4429) ([#4297](https://github.com/EQEmu/Server/pull/4297)) @Akkadius 2024-07-30
|
||||
|
||||
### Zoning
|
||||
|
||||
* Improve zone routing ([#4428](https://github.com/EQEmu/Server/pull/4428)) @Akkadius 2024-07-30
|
||||
|
||||
## [22.53.1] 6/16/2024
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix trader mode ([#4397](https://github.com/EQEmu/Server/pull/4397)) @joligario 2024-06-17
|
||||
|
||||
## [22.53.0] 6/14/2024
|
||||
|
||||
### Bug
|
||||
|
||||
* Anon players should not show in /who all ([#4392](https://github.com/EQEmu/Server/pull/4392)) @fryguy503 2024-06-14
|
||||
* Escape should put player into SOS if owned. ([#4388](https://github.com/EQEmu/Server/pull/4388)) @fryguy503 2024-06-07
|
||||
* Prevent Resurrection Spells from being resisted ([#4393](https://github.com/EQEmu/Server/pull/4393)) @fryguy503 2024-06-14
|
||||
|
||||
### Code
|
||||
|
||||
* Cleanup Account Status Code ([#4376](https://github.com/EQEmu/Server/pull/4376)) @Kinglykrab 2024-06-02
|
||||
* Cleanup Body Type Code ([#4366](https://github.com/EQEmu/Server/pull/4366)) @Kinglykrab 2024-06-02
|
||||
* Cleanup Object Type Code ([#4375](https://github.com/EQEmu/Server/pull/4375)) @Kinglykrab 2024-06-14
|
||||
* Remove unused code in emu_constants.h ([#4384](https://github.com/EQEmu/Server/pull/4384)) @Kinglykrab 2024-06-14
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix #goto Target ([#4382](https://github.com/EQEmu/Server/pull/4382)) @Kinglykrab 2024-06-03
|
||||
* Fix Swarm Pet Damage Messages ([#4383](https://github.com/EQEmu/Server/pull/4383)) @Kinglykrab 2024-06-04
|
||||
* Fix for players having empty bazaar window dropdown list, even though trader is tagged as a trader. ([#4391](https://github.com/EQEmu/Server/pull/4391)) @neckkola 2024-06-14
|
||||
* Fix potential trader crash when serialized item not found ([#4386](https://github.com/EQEmu/Server/pull/4386)) @joligario 2024-06-14
|
||||
|
||||
### Rules
|
||||
|
||||
* Add Invisible Augment Rules ([#4385](https://github.com/EQEmu/Server/pull/4385)) @Kinglykrab 2024-06-14
|
||||
* Classic Harm Touch Formula ([#4394](https://github.com/EQEmu/Server/pull/4394)) @fryguy503 2024-06-14
|
||||
* Mend/Sneak allow success tuning ([#4390](https://github.com/EQEmu/Server/pull/4390)) @fryguy503 2024-06-14
|
||||
* Snare Override Movement Bonus ([#4381](https://github.com/EQEmu/Server/pull/4381)) @fryguy503 2024-06-02
|
||||
|
||||
## [22.52.0] 6/1/2024
|
||||
|
||||
### Code
|
||||
|
||||
+38
-139
@@ -29,7 +29,10 @@
|
||||
#include "../../common/content/world_content_service.h"
|
||||
#include "../../common/zone_store.h"
|
||||
#include "../../common/path_manager.h"
|
||||
#include "../../common/repositories/base_data_repository.h"
|
||||
#include "../../common/repositories/db_str_repository.h"
|
||||
#include "../../common/repositories/skill_caps_repository.h"
|
||||
#include "../../common/repositories/spells_new_repository.h"
|
||||
#include "../../common/file.h"
|
||||
#include "../../common/events/player_event_logs.h"
|
||||
#include "../../common/skill_caps.h"
|
||||
@@ -99,25 +102,22 @@ int main(int argc, char **argv)
|
||||
->LoadLogDatabaseSettings()
|
||||
->StartFileLogs();
|
||||
|
||||
std::string arg_1;
|
||||
std::string export_type;
|
||||
|
||||
if (argv[1]) {
|
||||
arg_1 = argv[1];
|
||||
export_type = argv[1];
|
||||
}
|
||||
|
||||
if (arg_1 == "spells") {
|
||||
if (Strings::EqualFold(export_type, "spells")) {
|
||||
ExportSpells(&content_db);
|
||||
return 0;
|
||||
}
|
||||
if (arg_1 == "skills") {
|
||||
} else if (Strings::EqualFold(export_type, "skills")) {
|
||||
ExportSkillCaps(&content_db);
|
||||
return 0;
|
||||
}
|
||||
if (arg_1 == "basedata") {
|
||||
} else if (Strings::EqualFold(export_type, "basedata") || Strings::EqualFold(export_type, "base_data")) {
|
||||
ExportBaseData(&content_db);
|
||||
return 0;
|
||||
}
|
||||
if (arg_1 == "dbstring") {
|
||||
} else if (Strings::EqualFold(export_type, "dbstr") || Strings::EqualFold(export_type, "dbstring")) {
|
||||
ExportDBStrings(&database);
|
||||
return 0;
|
||||
}
|
||||
@@ -132,180 +132,79 @@ int main(int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExportSpells(SharedDatabase *db)
|
||||
void ExportSpells(SharedDatabase* db)
|
||||
{
|
||||
LogInfo("Exporting Spells");
|
||||
|
||||
std::string file = fmt::format("{}/export/spells_us.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/spells_us.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/spells_us.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string query = "SELECT * FROM spells_new ORDER BY id";
|
||||
auto results = db->QueryDatabase(query);
|
||||
const auto& lines = SpellsNewRepository::GetSpellFileLines(*db);
|
||||
|
||||
if (results.Success()) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
std::string line;
|
||||
unsigned int fields = results.ColumnCount();
|
||||
for (unsigned int i = 0; i < fields; ++i) {
|
||||
if (i != 0) {
|
||||
line.push_back('^');
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
if (row[i] != nullptr) {
|
||||
line += row[i];
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
fprintf(f, "%s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
file.close();
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
bool SkillUsable(SharedDatabase* db, int skill_id, int class_id)
|
||||
{
|
||||
const auto& l = SkillCapsRepository::GetWhere(
|
||||
*db,
|
||||
fmt::format(
|
||||
"`class_id` = {} AND `skill_id` = {} ORDER BY `cap` DESC LIMIT 1",
|
||||
class_id,
|
||||
skill_id
|
||||
)
|
||||
);
|
||||
|
||||
return !l.empty();
|
||||
}
|
||||
|
||||
uint32 GetSkill(SharedDatabase* db, int skill_id, int class_id, int level)
|
||||
{
|
||||
const auto& l = SkillCapsRepository::GetWhere(
|
||||
*db,
|
||||
fmt::format(
|
||||
"`class_id` = {} AND `skill_id` = {} AND `level` = {}",
|
||||
class_id,
|
||||
skill_id,
|
||||
level
|
||||
)
|
||||
);
|
||||
|
||||
if (l.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto e = l.front();
|
||||
|
||||
return e.cap;
|
||||
LogInfo("Exported [{}] Spell{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
void ExportSkillCaps(SharedDatabase* db)
|
||||
{
|
||||
LogInfo("Exporting Skill Caps");
|
||||
|
||||
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
|
||||
for (uint8 skill_id = EQ::skills::Skill1HBlunt; skill_id <= EQ::skills::Skill2HPiercing; skill_id++) {
|
||||
if (SkillUsable(db, skill_id, class_id)) {
|
||||
uint32 previous_cap = 0;
|
||||
const auto& lines = SkillCapsRepository::GetSkillCapFileLines(*db);
|
||||
|
||||
for (
|
||||
uint8 level = 1;
|
||||
level <= SkillCaps::GetSkillCapMaxLevel(class_id, static_cast<EQ::skills::SkillType>(skill_id));
|
||||
level++
|
||||
) {
|
||||
uint32 cap = GetSkill(db, skill_id, class_id, level);
|
||||
if (cap < previous_cap) {
|
||||
cap = previous_cap;
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
file << fmt::format("{}^{}^{}^{}^0", class_id, skill_id, level, cap) << std::endl;
|
||||
|
||||
previous_cap = cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
file.close();
|
||||
|
||||
LogInfo("Exported [{}] Skill Cap{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
void ExportBaseData(SharedDatabase *db)
|
||||
{
|
||||
LogInfo("Exporting Base Data");
|
||||
|
||||
std::string file = fmt::format("{}/export/BaseData.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/BaseData.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/BaseData.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string query = "SELECT * FROM base_data ORDER BY level, class";
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (results.Success()) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
std::string line;
|
||||
unsigned int fields = results.ColumnCount();
|
||||
for (unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) {
|
||||
if (rowIndex != 0) {
|
||||
line.push_back('^');
|
||||
}
|
||||
const auto& lines = BaseDataRepository::GetBaseDataFileLines(*db);
|
||||
|
||||
if (row[rowIndex] != nullptr) {
|
||||
line += row[rowIndex];
|
||||
}
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
fprintf(f, "%s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
fclose(f);
|
||||
file.close();
|
||||
|
||||
LogInfo("Exported [{}] Base Data Entr{}", lines.size(), lines.size() != 1 ? "ies" : "y");
|
||||
}
|
||||
|
||||
void ExportDBStrings(SharedDatabase *db)
|
||||
{
|
||||
LogInfo("Exporting DB Strings");
|
||||
|
||||
std::string file = fmt::format("{}/export/dbstr_us.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/dbstr_us.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/dbstr_us.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "Major^Minor^String(New)\n");
|
||||
const std::string query = "SELECT * FROM db_str ORDER BY id, type";
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (results.Success()) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
std::string line;
|
||||
unsigned int fields = results.ColumnCount();
|
||||
for (unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) {
|
||||
if (rowIndex != 0) {
|
||||
line.push_back('^');
|
||||
}
|
||||
const auto& lines = DbStrRepository::GetDBStrFileLines(*db);
|
||||
|
||||
if (row[rowIndex] != nullptr) {
|
||||
line += row[rowIndex];
|
||||
}
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
fprintf(f, "%s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
fclose(f);
|
||||
file.close();
|
||||
|
||||
LogInfo("Exported [{}] Database String{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
|
||||
SET(common_sources
|
||||
base_packet.cpp
|
||||
bazaar.cpp
|
||||
bodytypes.cpp
|
||||
classes.cpp
|
||||
cli/eqemu_command_handler.cpp
|
||||
compression.cpp
|
||||
@@ -61,6 +62,7 @@ SET(common_sources
|
||||
mutex.cpp
|
||||
mysql_request_result.cpp
|
||||
mysql_request_row.cpp
|
||||
mysql_stmt.cpp
|
||||
opcode_map.cpp
|
||||
opcodemgr.cpp
|
||||
packet_dump.cpp
|
||||
@@ -157,6 +159,7 @@ SET(repositories
|
||||
repositories/base/base_bugs_repository.h
|
||||
repositories/base/base_bug_reports_repository.h
|
||||
repositories/base/base_buyer_repository.h
|
||||
repositories/base/base_buyer_trade_items_repository.h
|
||||
repositories/base/base_character_activities_repository.h
|
||||
repositories/base/base_character_alternate_abilities_repository.h
|
||||
repositories/base/base_character_alt_currency_repository.h
|
||||
@@ -338,7 +341,8 @@ SET(repositories
|
||||
repositories/books_repository.h
|
||||
repositories/bugs_repository.h
|
||||
repositories/bug_reports_repository.h
|
||||
repositories/buyer_repository.h
|
||||
repositories/buyer_buy_lines_repository.h
|
||||
repositories/buyer_trade_items_repository.h
|
||||
repositories/character_activities_repository.h
|
||||
repositories/character_alternate_abilities_repository.h
|
||||
repositories/character_alt_currency_repository.h
|
||||
@@ -583,6 +587,7 @@ SET(common_headers
|
||||
mutex.h
|
||||
mysql_request_result.h
|
||||
mysql_request_row.h
|
||||
mysql_stmt.h
|
||||
op_codes.h
|
||||
opcode_dispatch.h
|
||||
opcodemgr.h
|
||||
|
||||
+3
-2
@@ -47,7 +47,7 @@ Bazaar::GetSearchResults(
|
||||
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
|
||||
}
|
||||
if (search.min_cost != 0) {
|
||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost));
|
||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost * 1000));
|
||||
}
|
||||
if (search.max_cost != 0) {
|
||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
||||
@@ -235,7 +235,8 @@ Bazaar::GetSearchResults(
|
||||
std::vector<ItemSearchType> item_search_types = {
|
||||
{EQ::item::ItemType::ItemTypeAll, true},
|
||||
{EQ::item::ItemType::ItemTypeBook, item->ItemClass == EQ::item::ItemType::ItemTypeBook},
|
||||
{EQ::item::ItemType::ItemTypeContainer, item->ItemClass == EQ::item::ItemType::ItemTypeContainer},
|
||||
{EQ::item::ItemType::ItemTypeContainer, item->ItemClass == EQ::item::ItemType::ItemTypeContainer ||
|
||||
item->IsClassBag()},
|
||||
{EQ::item::ItemType::ItemTypeAllEffects, item->Scroll.Effect > 0 && item->Scroll.Effect < 65000},
|
||||
{EQ::item::ItemType::ItemTypeUnknown9, item->Worn.Effect == 998},
|
||||
{EQ::item::ItemType::ItemTypeUnknown10, item->Worn.Effect >= 1298 && item->Worn.Effect <= 1307},
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/bodytypes.h"
|
||||
|
||||
std::string BodyType::GetName(uint8 body_type_id)
|
||||
{
|
||||
return IsValid(body_type_id) ? body_type_names[body_type_id] : "UNKNOWN BODY TYPE";
|
||||
}
|
||||
|
||||
bool BodyType::IsValid(uint8 body_type_id)
|
||||
{
|
||||
return body_type_names.find(body_type_id) != body_type_names.end();
|
||||
}
|
||||
+90
-46
@@ -18,52 +18,96 @@
|
||||
#ifndef BODYTYPES_H
|
||||
#define BODYTYPES_H
|
||||
|
||||
typedef enum {
|
||||
BT_Humanoid = 1,
|
||||
BT_Lycanthrope = 2,
|
||||
BT_Undead = 3,
|
||||
BT_Giant = 4,
|
||||
BT_Construct = 5,
|
||||
BT_Extraplanar = 6,
|
||||
BT_Magical = 7, //this name might be a bit off,
|
||||
BT_SummonedUndead = 8,
|
||||
BT_RaidGiant = 9, //Velious era Raid Giant
|
||||
BT_RaidColdain = 10, //Velious era Raid Coldain
|
||||
BT_NoTarget = 11, //no name, can't target this bodytype
|
||||
BT_Vampire = 12,
|
||||
BT_Atenha_Ra = 13,
|
||||
BT_Greater_Akheva = 14,
|
||||
BT_Khati_Sha = 15,
|
||||
BT_Seru = 16,
|
||||
BT_Grieg_Veneficus = 17,
|
||||
BT_Draz_Nurakk = 18,
|
||||
BT_Zek = 19, //"creatures from the Plane of War."
|
||||
BT_Luggald = 20,
|
||||
BT_Animal = 21,
|
||||
BT_Insect = 22,
|
||||
BT_Monster = 23,
|
||||
BT_Summoned = 24, //Elemental?
|
||||
BT_Plant = 25,
|
||||
BT_Dragon = 26,
|
||||
BT_Summoned2 = 27,
|
||||
BT_Summoned3 = 28,
|
||||
BT_Dragon2 = 29, //database data indicates this is a dragon type (kunark and DoN?)
|
||||
BT_VeliousDragon = 30, //might not be a tight set
|
||||
BT_Familiar = 31,
|
||||
BT_Dragon3 = 32,
|
||||
BT_Boxes = 33,
|
||||
BT_Muramite = 34, //tribal dudes
|
||||
// ...
|
||||
BT_NoTarget2 = 60,
|
||||
// ...
|
||||
BT_SwarmPet = 63, //Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general.
|
||||
BT_MonsterSummon = 64,
|
||||
// 65, trap or effect related?
|
||||
BT_InvisMan = 66, //no name, seen on 'InvisMan', can be /targeted
|
||||
BT_Special = 67
|
||||
} bodyType;
|
||||
/* bodytypes above 64 make the mob not show up */
|
||||
#include "types.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
constexpr int format_as(bodyType type) { return static_cast<int>(type); }
|
||||
// body types above 64 make the mob invisible
|
||||
namespace BodyType {
|
||||
constexpr uint8 Humanoid = 1;
|
||||
constexpr uint8 Lycanthrope = 2;
|
||||
constexpr uint8 Undead = 3;
|
||||
constexpr uint8 Giant = 4;
|
||||
constexpr uint8 Construct = 5;
|
||||
constexpr uint8 Extraplanar = 6;
|
||||
constexpr uint8 Magical = 7; // this name might be a bit off,
|
||||
constexpr uint8 SummonedUndead = 8;
|
||||
constexpr uint8 RaidGiant = 9; // Velious era Raid Giant
|
||||
constexpr uint8 RaidColdain = 10; // Velious era Raid Coldain
|
||||
constexpr uint8 NoTarget = 11; // no name, can't target this bodytype
|
||||
constexpr uint8 Vampire = 12;
|
||||
constexpr uint8 AtenHaRa = 13;
|
||||
constexpr uint8 GreaterAkheva = 14;
|
||||
constexpr uint8 KhatiSha = 15;
|
||||
constexpr uint8 Seru = 16;
|
||||
constexpr uint8 GriegVeneficus = 17;
|
||||
constexpr uint8 DrazNurakk = 18;
|
||||
constexpr uint8 Zek = 19; //"creatures from the Plane of War."
|
||||
constexpr uint8 Luggald = 20;
|
||||
constexpr uint8 Animal = 21;
|
||||
constexpr uint8 Insect = 22;
|
||||
constexpr uint8 Monster = 23;
|
||||
constexpr uint8 Summoned = 24; // Elemental?
|
||||
constexpr uint8 Plant = 25;
|
||||
constexpr uint8 Dragon = 26;
|
||||
constexpr uint8 Summoned2 = 27;
|
||||
constexpr uint8 Summoned3 = 28;
|
||||
constexpr uint8 Dragon2 = 29; // database data indicates this is a dragon type (Kunark and DoN?)
|
||||
constexpr uint8 VeliousDragon = 30; // might not be a tight set
|
||||
constexpr uint8 Familiar = 31;
|
||||
constexpr uint8 Dragon3 = 32;
|
||||
constexpr uint8 Boxes = 33;
|
||||
constexpr uint8 Muramite = 34; // tribal dudes
|
||||
constexpr uint8 NoTarget2 = 60;
|
||||
constexpr uint8 SwarmPet = 63; // Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general.
|
||||
constexpr uint8 MonsterSummon = 64;
|
||||
constexpr uint8 InvisibleMan = 66; // no name, seen on 'InvisMan', can be /targeted
|
||||
constexpr uint8 Special = 67;
|
||||
|
||||
std::string GetName(uint8 body_type_id);
|
||||
bool IsValid(uint8 body_type_id);
|
||||
}
|
||||
|
||||
static std::map<uint8, std::string> body_type_names = {
|
||||
{ BodyType::Humanoid, "Humanoid" },
|
||||
{ BodyType::Lycanthrope, "Lycanthrope" },
|
||||
{ BodyType::Undead, "Undead" },
|
||||
{ BodyType::Giant, "Giant" },
|
||||
{ BodyType::Construct, "Construct" },
|
||||
{ BodyType::Extraplanar, "Extraplanar" },
|
||||
{ BodyType::Magical, "Magical" },
|
||||
{ BodyType::SummonedUndead, "Summoned Undead" },
|
||||
{ BodyType::RaidGiant, "Raid Giant" },
|
||||
{ BodyType::RaidColdain, "Raid Coldain" },
|
||||
{ BodyType::NoTarget, "Untargetable" },
|
||||
{ BodyType::Vampire, "Vampire" },
|
||||
{ BodyType::AtenHaRa, "Aten Ha Ra" },
|
||||
{ BodyType::GreaterAkheva, "Greater Akheva" },
|
||||
{ BodyType::KhatiSha, "Khati Sha" },
|
||||
{ BodyType::Seru, "Seru" },
|
||||
{ BodyType::GriegVeneficus, "Grieg Veneficus" },
|
||||
{ BodyType::DrazNurakk, "Draz Nurakk" },
|
||||
{ BodyType::Zek, "Zek" },
|
||||
{ BodyType::Luggald, "Luggald" },
|
||||
{ BodyType::Animal, "Animal" },
|
||||
{ BodyType::Insect, "Insect" },
|
||||
{ BodyType::Monster, "Monster" },
|
||||
{ BodyType::Summoned, "Summoned" },
|
||||
{ BodyType::Plant, "Plant" },
|
||||
{ BodyType::Dragon, "Dragon" },
|
||||
{ BodyType::Summoned2, "Summoned 2" },
|
||||
{ BodyType::Summoned3, "Summoned 3" },
|
||||
{ BodyType::Dragon2, "Dragon 2" },
|
||||
{ BodyType::VeliousDragon, "Velious Dragon" },
|
||||
{ BodyType::Familiar, "Familiar" },
|
||||
{ BodyType::Dragon3, "Dragon 3" },
|
||||
{ BodyType::Boxes, "Boxes" },
|
||||
{ BodyType::Muramite, "Muramite" },
|
||||
{ BodyType::NoTarget2, "Untargetable 2" },
|
||||
{ BodyType::SwarmPet, "Swarm Pet" },
|
||||
{ BodyType::MonsterSummon, "Monster Summon" },
|
||||
{ BodyType::InvisibleMan, "Invisible Man" },
|
||||
{ BodyType::Special, "Special" },
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../rulesys.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
#include "../repositories/instance_list_repository.h"
|
||||
#include "../zone_store.h"
|
||||
|
||||
|
||||
WorldContentService::WorldContentService()
|
||||
@@ -183,8 +184,8 @@ void WorldContentService::ReloadContentFlags()
|
||||
}
|
||||
|
||||
SetContentFlags(set_content_flags);
|
||||
LoadZones();
|
||||
LoadStaticGlobalZoneInstances();
|
||||
zone_store.LoadZones(*m_content_database);
|
||||
}
|
||||
|
||||
Database *WorldContentService::GetDatabase() const
|
||||
@@ -236,18 +237,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
|
||||
ReloadContentFlags();
|
||||
}
|
||||
|
||||
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
|
||||
//
|
||||
// example # 1
|
||||
// lavastorm (pre-don) version 0 (classic)
|
||||
// lavastorm (don) version 1
|
||||
// we want to route players to the correct version of lavastorm based on the current server side expansion
|
||||
// in order to do that the simplest and cleanest way we intercept the zoning process and route players to an "instance" of the zone
|
||||
// the reason why we're doing this is because all of the zoning logic already is handled by two keys "zone_id" and "instance_id"
|
||||
// we can leverage static, never expires instances to handle this but to the client they don't see it any other way than a public normal zone
|
||||
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
|
||||
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
|
||||
// we decide to route the client to the correct version of the zone based on the current server side expansion
|
||||
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
{
|
||||
auto r = FindZone(zc->zoneID, zc->instanceID);
|
||||
@@ -261,93 +250,72 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
// LoadStaticGlobalZoneInstances loads all static global zone instances
|
||||
// these are zones that are never set to expire and are global
|
||||
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
|
||||
WorldContentService * WorldContentService::LoadStaticGlobalZoneInstances()
|
||||
WorldContentService *WorldContentService::LoadStaticGlobalZoneInstances()
|
||||
{
|
||||
m_zone_instances = InstanceListRepository::GetWhere(*GetDatabase(), fmt::format("never_expires = 1 AND is_global = 1"));
|
||||
m_zone_static_instances = InstanceListRepository::GetWhere(
|
||||
*GetDatabase(),
|
||||
fmt::format("never_expires = 1 AND is_global = 1")
|
||||
);
|
||||
|
||||
LogInfo("Loaded [{}] zone_instances", m_zone_instances.size());
|
||||
LogInfo("Loaded [{}] zone_instances", m_zone_static_instances.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// LoadZones sets the zones for the world content service
|
||||
// this is used for zone routing middleware
|
||||
// we pull the zone list from the zone repository and feed from the zone store for now
|
||||
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
|
||||
WorldContentService * WorldContentService::LoadZones()
|
||||
{
|
||||
m_zones = ZoneRepository::All(*GetContentDatabase());
|
||||
|
||||
LogInfo("Loaded [{}] zones", m_zones.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// FindZone is critical to the zone routing middleware and any logic that needs to route players to the correct zone
|
||||
// era contextual routing, multiple version of zones, etc
|
||||
// FindZone handles content and context aware zone routing (middleware)
|
||||
//
|
||||
// this is a middleware function that is meant to be used in the zone change process
|
||||
// this hooks all core zone changes within the server and routes the player to the correct zone
|
||||
// returning a zone_id of non-zero means the middleware will route the player
|
||||
// returning a zone_id of 0 means the middleware will not route the player
|
||||
// this is useful for handling multiple versions of the same zone
|
||||
//
|
||||
// implementation >
|
||||
// the zoning and process spawning logic already is handled by two keys "zone_id" and "instance_id"
|
||||
// we leverage static, never expires instances to handle this and client still sees it as a normal zone
|
||||
//
|
||||
// content awareness >
|
||||
// simply use the zone_id, server content settings and the middleware will handle the rest
|
||||
// you don't have to think about instances in any data tables (use instance_id 0)
|
||||
// you don't have to keep track of instance ids in scripts (use instance_id 0)
|
||||
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
|
||||
// we decide to route the client to the correct version of the zone based on the current server side expansion
|
||||
//
|
||||
// example >
|
||||
// we want to route players to the correct version of lavastorm based on the current server side expansion (DoesZonePassContentFiltering)
|
||||
// lavastorm (pre-don) version 0 (classic)
|
||||
// zone table entry for version = 0, min_expansion = 0, max_expansion = 8
|
||||
// instance_list table entry for lavastorm has version = 0, is_global = 1, never_expires = 1
|
||||
// lavastorm (don) version 1
|
||||
// zone table entry for version = 1, min_expansion = 9, max_expansion = 99
|
||||
// instance_list table entry for lavastorm has version = 1, is_global = 1, never_expires = 1
|
||||
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
|
||||
{
|
||||
// if there's an active dynamic instance, we don't need to route
|
||||
if (instance_id > 0) {
|
||||
auto inst = InstanceListRepository::FindOne(*GetDatabase(), instance_id);
|
||||
if (inst.id != 0 && !inst.is_global && !inst.never_expires) {
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
for (const auto &z: zone_store.GetZones()) {
|
||||
for (auto &i: m_zone_static_instances) {
|
||||
if (
|
||||
z.zoneidnumber == zone_id &&
|
||||
DoesZonePassContentFiltering(z) &&
|
||||
i.zone == zone_id &&
|
||||
i.version == z.version) {
|
||||
|
||||
for (auto &z: m_zones) {
|
||||
if (z.zoneidnumber == zone_id) {
|
||||
auto f = ContentFlags{
|
||||
.min_expansion = z.min_expansion,
|
||||
.max_expansion = z.max_expansion,
|
||||
.content_flags = z.content_flags,
|
||||
.content_flags_disabled = z.content_flags_disabled
|
||||
};
|
||||
|
||||
if (DoesPassContentFiltering(f)) {
|
||||
LogInfo(
|
||||
"Attempting to route player to zone [{}] ({}) version [{}] long_name [{}]",
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name
|
||||
);
|
||||
|
||||
// first pass, explicit match on public static global zone instances
|
||||
for (auto &i: m_zone_instances) {
|
||||
if (i.zone == zone_id && i.version == z.version) {
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
i.notes
|
||||
);
|
||||
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = i,
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
if (instance_id > 0 && i.id != instance_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Routed player to non-instance zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
"Routed player to public static instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
z.note
|
||||
i.notes
|
||||
);
|
||||
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = InstanceListRepository::NewEntity(),
|
||||
.instance = i,
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
@@ -359,7 +327,7 @@ WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id
|
||||
|
||||
bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
|
||||
{
|
||||
for (auto &i: m_zone_instances) {
|
||||
for (auto &i: m_zone_static_instances) {
|
||||
if (i.id == instance_id) {
|
||||
return true;
|
||||
}
|
||||
@@ -367,3 +335,15 @@ bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WorldContentService::DoesZonePassContentFiltering(const ZoneRepository::Zone &z)
|
||||
{
|
||||
auto f = ContentFlags{
|
||||
.min_expansion = z.min_expansion,
|
||||
.max_expansion = z.max_expansion,
|
||||
.content_flags = z.content_flags,
|
||||
.content_flags_disabled = z.content_flags_disabled
|
||||
};
|
||||
|
||||
return DoesPassContentFiltering(f);
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ public:
|
||||
WorldContentService * SetExpansionContext();
|
||||
|
||||
bool DoesPassContentFiltering(const ContentFlags& f);
|
||||
bool DoesZonePassContentFiltering(const ZoneRepository::Zone& z);
|
||||
|
||||
WorldContentService * SetDatabase(Database *database);
|
||||
Database *GetDatabase() const;
|
||||
@@ -189,10 +190,8 @@ private:
|
||||
Database *m_content_database;
|
||||
|
||||
// holds a record of the zone table from the database
|
||||
std::vector<ZoneRepository::Zone> m_zones = {};
|
||||
WorldContentService *LoadStaticGlobalZoneInstances();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
|
||||
WorldContentService * LoadZones();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_static_instances;
|
||||
};
|
||||
|
||||
extern WorldContentService content_service;
|
||||
|
||||
+57
-14
@@ -66,6 +66,7 @@
|
||||
#endif
|
||||
|
||||
#include "database.h"
|
||||
#include "data_verification.h"
|
||||
#include "eq_packet_structs.h"
|
||||
#include "extprofile.h"
|
||||
#include "strings.h"
|
||||
@@ -77,6 +78,8 @@
|
||||
#include "zone_store.h"
|
||||
#include "repositories/merchantlist_temp_repository.h"
|
||||
#include "repositories/bot_data_repository.h"
|
||||
#include "repositories/trader_repository.h"
|
||||
#include "repositories/buyer_repository.h"
|
||||
|
||||
extern Client client;
|
||||
|
||||
@@ -282,16 +285,31 @@ bool Database::SetAccountStatus(const std::string& account_name, int16 status)
|
||||
|
||||
bool Database::ReserveName(uint32 account_id, const std::string& name)
|
||||
{
|
||||
const auto& l = CharacterDataRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`name` = '{}'",
|
||||
Strings::Escape(name)
|
||||
)
|
||||
const std::string& where_filter = fmt::format(
|
||||
"`name` = '{}'",
|
||||
Strings::Escape(name)
|
||||
);
|
||||
|
||||
if (!l.empty()) {
|
||||
LogInfo("Account: [{}] tried to request name: [{}], but it is already taken", account_id, name);
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
const auto& b = BotDataRepository::GetWhere(*this, where_filter);
|
||||
|
||||
if (!b.empty()) {
|
||||
LogInfo("Account [{}] requested name [{}] but name is already taken by a bot", account_id, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& c = CharacterDataRepository::GetWhere(*this, where_filter);
|
||||
|
||||
if (!c.empty()) {
|
||||
LogInfo("Account [{}] requested name [{}] but name is already taken by a character", account_id, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& n = NpcTypesRepository::GetWhere(*this, where_filter);
|
||||
|
||||
if (!n.empty()) {
|
||||
LogInfo("Account [{}] requested name [{}] but name is already taken by an NPC", account_id, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -306,13 +324,15 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
|
||||
return false;
|
||||
}
|
||||
|
||||
const int guild_id = RuleI(Character, DefaultGuild);
|
||||
const uint32 guild_id = RuleI(Character, DefaultGuild);
|
||||
const uint8 guild_rank = EQ::Clamp(RuleI(Character, DefaultGuildRank), 0, 8);
|
||||
if (guild_id != 0) {
|
||||
if (e.id) {
|
||||
auto g = GuildMembersRepository::NewEntity();
|
||||
|
||||
g.char_id = e.id;
|
||||
g.guild_id = guild_id;
|
||||
g.rank_ = guild_rank;
|
||||
|
||||
GuildMembersRepository::InsertOne(*this, g);
|
||||
}
|
||||
@@ -1628,16 +1648,29 @@ uint32 Database::GetGuildIDByCharID(uint32 character_id)
|
||||
|
||||
uint32 Database::GetGroupIDByCharID(uint32 character_id)
|
||||
{
|
||||
const auto& e = GroupIdRepository::FindOne(*this, character_id);
|
||||
const auto& e = GroupIdRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`character_id` = {}",
|
||||
character_id
|
||||
)
|
||||
);
|
||||
|
||||
return e.character_id ? e.group_id : 0;
|
||||
return e.size() == 1 ? e.front().group_id : 0;
|
||||
}
|
||||
|
||||
uint32 Database::GetRaidIDByCharID(uint32 character_id)
|
||||
{
|
||||
const auto& e = RaidMembersRepository::FindOne(*this, character_id);
|
||||
|
||||
return e.charid ? e.raidid : 0;
|
||||
const auto& e = RaidMembersRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`charid` = {}",
|
||||
character_id
|
||||
)
|
||||
);
|
||||
|
||||
return e.size() == 1 ? e.front().raidid : 0;
|
||||
}
|
||||
|
||||
int64 Database::CountInvSnapshots()
|
||||
@@ -1827,7 +1860,7 @@ bool Database::CopyCharacter(
|
||||
|
||||
const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1);
|
||||
|
||||
std::vector<std::string> tables_to_zero_id = { "keyring", "data_buckets" };
|
||||
std::vector<std::string> tables_to_zero_id = { "keyring", "data_buckets", "character_instance_safereturns" };
|
||||
|
||||
TransactionBegin();
|
||||
|
||||
@@ -2104,3 +2137,13 @@ void Database::ClearGuildOnlineStatus()
|
||||
{
|
||||
GuildMembersRepository::ClearOnlineStatus(*this);
|
||||
}
|
||||
|
||||
void Database::ClearTraderDetails()
|
||||
{
|
||||
TraderRepository::Truncate(*this);
|
||||
}
|
||||
|
||||
void Database::ClearBuyerDetails()
|
||||
{
|
||||
BuyerRepository::DeleteBuyer(*this, 0);
|
||||
}
|
||||
|
||||
@@ -244,6 +244,8 @@ public:
|
||||
|
||||
void PurgeAllDeletedDataBuckets();
|
||||
void ClearGuildOnlineStatus();
|
||||
void ClearTraderDetails();
|
||||
void ClearBuyerDetails();
|
||||
|
||||
|
||||
/* Database Variables */
|
||||
|
||||
@@ -4947,7 +4947,7 @@ UPDATE `aa_ability` SET `auto_grant_enabled` = 1 WHERE `grant_only` = 0 AND `cha
|
||||
.version = 9237,
|
||||
.description = "2023_10_15_import_13th_floor.sql",
|
||||
.check = "SHOW COLUMNS FROM `items` LIKE 'bardeffect';",
|
||||
.condition = "contains",
|
||||
.condition = "missing",
|
||||
.match = "mediumint",
|
||||
.sql = R"(
|
||||
ALTER TABLE `items`
|
||||
@@ -5660,6 +5660,190 @@ ALTER TABLE `trader`
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `charid_slotid` (`char_id`, `slot_id`);
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9281,
|
||||
.description = "2024_06_24_update_buyer_support.sql",
|
||||
.check = "SHOW COLUMNS FROM `buyer` LIKE 'id'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `buyer`
|
||||
ADD COLUMN `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||
CHANGE COLUMN `charid` `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`,
|
||||
ADD COLUMN `char_entity_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_id`,
|
||||
ADD COLUMN `char_name` VARCHAR(64) NULL DEFAULT NULL AFTER `char_entity_id`,
|
||||
ADD COLUMN `char_zone_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_name`,
|
||||
ADD COLUMN `char_zone_instance_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_zone_id`,
|
||||
ADD COLUMN `transaction_date` DATETIME NULL DEFAULT NULL AFTER `char_zone_instance_id`,
|
||||
ADD COLUMN `welcome_message` VARCHAR(256) NULL DEFAULT NULL AFTER `transaction_date`,
|
||||
DROP COLUMN `buyslot`,
|
||||
DROP COLUMN `itemid`,
|
||||
DROP COLUMN `itemname`,
|
||||
DROP COLUMN `quantity`,
|
||||
DROP COLUMN `price`,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`) USING BTREE,
|
||||
ADD INDEX `charid` (`char_id`);
|
||||
|
||||
CREATE TABLE `buyer_buy_lines` (
|
||||
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`buyer_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`buy_slot_id` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_id` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_qty` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_price` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_icon` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`item_name` VARCHAR(64) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `buyerid_charid_buyslotid` (`buyer_id`, `char_id`, `buy_slot_id`) USING BTREE
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1;
|
||||
|
||||
CREATE TABLE `buyer_trade_items` (
|
||||
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`buyer_buy_lines_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`item_id` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_qty` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_icon` INT(11) NOT NULL DEFAULT '0',
|
||||
`item_name` VARCHAR(64) NOT NULL DEFAULT '0' COLLATE 'latin1_swedish_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `buyerbuylinesid` (`buyer_buy_lines_id`) USING BTREE
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9282,
|
||||
.description = "2024_08_02_spell_buckets_comparison.sql",
|
||||
.check = "SHOW COLUMNS FROM `spell_buckets` LIKE 'bucket_comparison'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `spell_buckets`
|
||||
CHANGE COLUMN `spellid` `spell_id` int UNSIGNED NOT NULL FIRST,
|
||||
CHANGE COLUMN `key` `bucket_name` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `spell_id`,
|
||||
CHANGE COLUMN `value` `bucket_value` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `bucket_name`,
|
||||
ADD COLUMN `bucket_comparison` tinyint UNSIGNED NOT NULL DEFAULT 0 AFTER `bucket_value`,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`spell_id`) USING BTREE;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9283,
|
||||
.description = "2024_08_05_fix_client_hotbar",
|
||||
.check = "SHOW COLUMNS FROM `inventory` LIKE 'guid'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `inventory`
|
||||
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
|
||||
ALTER TABLE `inventory_snapshots`
|
||||
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9284,
|
||||
.description = "2024_10_08_character_exp_modifiers_default.sql",
|
||||
.check = "SHOW CREATE TABLE `character_exp_modifiers`",
|
||||
.condition = "contains",
|
||||
.match = "`exp_modifier` float NOT NULL,",
|
||||
.sql = R"(
|
||||
ALTER TABLE `character_exp_modifiers`
|
||||
MODIFY COLUMN `aa_modifier` float NOT NULL DEFAULT 1.0 AFTER `instance_version`,
|
||||
MODIFY COLUMN `exp_modifier` float NOT NULL DEFAULT 1.0 AFTER `aa_modifier`;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9285,
|
||||
.description = "2024_11_08_data_buckets_indexes.sql",
|
||||
.check = "SHOW CREATE TABLE `data_buckets`",
|
||||
.condition = "missing",
|
||||
.match = "idx_character_expires",
|
||||
.sql = R"(
|
||||
CREATE INDEX idx_character_expires ON data_buckets (character_id, expires);
|
||||
CREATE INDEX idx_npc_expires ON data_buckets (npc_id, expires);
|
||||
CREATE INDEX idx_bot_expires ON data_buckets (bot_id, expires);
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9286,
|
||||
.description = "2024_10_24_sharedbank_guid_primary_key.sql",
|
||||
.check = "SHOW COLUMN FROM `sharedbank` LIKE 'guid'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `sharedbank`
|
||||
CHANGE COLUMN `acctid` `account_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST,
|
||||
CHANGE COLUMN `slotid` `slot_id` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `account_id`,
|
||||
CHANGE COLUMN `itemid` `item_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `slot_id`,
|
||||
CHANGE COLUMN `augslot1` `augment_one` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`,
|
||||
CHANGE COLUMN `augslot2` `augment_two` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_one`,
|
||||
CHANGE COLUMN `augslot3` `augment_three` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_two`,
|
||||
CHANGE COLUMN `augslot4` `augment_four` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_three`,
|
||||
CHANGE COLUMN `augslot5` `augment_five` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_four`,
|
||||
CHANGE COLUMN `augslot6` `augment_six` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`,
|
||||
MODIFY COLUMN `charges` smallint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `item_id`,
|
||||
ADD COLUMN `color` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`,
|
||||
ADD COLUMN `ornament_icon` int(11) UNSIGNED NOT NULL AFTER `custom_data`,
|
||||
ADD COLUMN `ornament_idfile` int(11) UNSIGNED NOT NULL AFTER `ornament_icon`,
|
||||
ADD COLUMN `ornament_hero_model` int(11) NOT NULL AFTER `ornament_idfile`,
|
||||
ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_model`,
|
||||
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9287,
|
||||
.description = "2024_10_24_inventory_changes.sql",
|
||||
.check = "SHOW COLUMN FROM `inventory` LIKE 'charid'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `inventory`
|
||||
CHANGE COLUMN `charid` `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST,
|
||||
CHANGE COLUMN `slotid` `slot_id` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
||||
CHANGE COLUMN `itemid` `item_id` int(11) UNSIGNED NULL DEFAULT 0 AFTER `slot_id`,
|
||||
CHANGE COLUMN `augslot1` `augment_one` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `color`,
|
||||
CHANGE COLUMN `augslot2` `augment_two` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_one`,
|
||||
CHANGE COLUMN `augslot3` `augment_three` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_two`,
|
||||
CHANGE COLUMN `augslot4` `augment_four` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_three`,
|
||||
CHANGE COLUMN `augslot5` `augment_five` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_four`,
|
||||
CHANGE COLUMN `augslot6` `augment_six` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`,
|
||||
CHANGE COLUMN `ornamenticon` `ornament_icon` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `custom_data`,
|
||||
CHANGE COLUMN `ornamentidfile` `ornament_idfile` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_icon`,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`character_id`, `slot_id`) USING BTREE;
|
||||
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 251) + 4010) WHERE `slot_id` BETWEEN 251 AND 260; -- Bag 1
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 261) + 4210) WHERE `slot_id` BETWEEN 261 AND 270; -- Bag 2
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 271) + 4410) WHERE `slot_id` BETWEEN 271 AND 280; -- Bag 3
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 281) + 4610) WHERE `slot_id` BETWEEN 281 AND 290; -- Bag 4
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 291) + 4810) WHERE `slot_id` BETWEEN 291 AND 300; -- Bag 5
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 301) + 5010) WHERE `slot_id` BETWEEN 301 AND 310; -- Bag 6
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 311) + 5210) WHERE `slot_id` BETWEEN 311 AND 320; -- Bag 7
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 321) + 5410) WHERE `slot_id` BETWEEN 321 AND 330; -- Bag 8
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 331) + 5610) WHERE `slot_id` BETWEEN 331 AND 340; -- Bag 9
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 341) + 5810) WHERE `slot_id` BETWEEN 341 AND 350; -- Bag 10
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 351) + 6010) WHERE `slot_id` BETWEEN 351 AND 360; -- Cursor Bag
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2270; -- Bank Bags
|
||||
UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2550; -- Shared Bank Bags
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9288,
|
||||
.description = "2024_10_24_merchantlist_temp_uncap.sql",
|
||||
.check = "SHOW CREATE TABLE `merchantlist_temp`",
|
||||
.condition = "contains",
|
||||
.match = "`slot` tinyint(3)",
|
||||
.sql = R"(
|
||||
ALTER TABLE `merchantlist_temp`
|
||||
MODIFY COLUMN `slot` int UNSIGNED NOT NULL DEFAULT 0 AFTER `npcid`;
|
||||
)"
|
||||
}
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
|
||||
@@ -150,6 +150,17 @@ ADD COLUMN `augment_six` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five
|
||||
.sql = R"(
|
||||
ALTER TABLE `bot_data`
|
||||
ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9045,
|
||||
.description = "2024_08_05_bot_spells_entries_unsigned_spell_id.sql",
|
||||
.check = "SHOW COLUMNS FROM `bot_spells_entries` LIKE 'spell_id'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `bot_spells_entries`
|
||||
CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_spells_id`;
|
||||
)"
|
||||
}
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace DatabaseSchema {
|
||||
{
|
||||
return {
|
||||
{"adventure_stats", "player_id"},
|
||||
{"buyer", "charid"},
|
||||
{"char_recipe_list", "char_id"},
|
||||
{"character_activities", "charid"},
|
||||
{"character_alt_currency", "char_id"},
|
||||
@@ -107,6 +106,8 @@ namespace DatabaseSchema {
|
||||
"adventure_details",
|
||||
"adventure_stats",
|
||||
"buyer",
|
||||
"buyer_buy_lines",
|
||||
"buyer_trade_items",
|
||||
"char_recipe_list",
|
||||
"character_activities",
|
||||
"character_alt_currency",
|
||||
@@ -325,6 +326,9 @@ namespace DatabaseSchema {
|
||||
"banned_ips",
|
||||
"bug_reports",
|
||||
"bugs",
|
||||
"buyer",
|
||||
"buyer_buy_lines",
|
||||
"buyer_trade_items",
|
||||
"completed_shared_task_activity_state",
|
||||
"completed_shared_task_members",
|
||||
"completed_shared_tasks",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "timer.h"
|
||||
|
||||
#include "dbcore.h"
|
||||
#include "mysql_stmt.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -436,3 +437,8 @@ MySQLRequestResult DBcore::QueryDatabaseMulti(const std::string &query)
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
mysql::PreparedStmt DBcore::Prepare(std::string query)
|
||||
{
|
||||
return mysql::PreparedStmt(*mysql, std::move(query), m_mutex);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#define CR_SERVER_GONE_ERROR 2006
|
||||
#define CR_SERVER_LOST 2013
|
||||
|
||||
namespace mysql { class PreparedStmt; }
|
||||
|
||||
class DBcore {
|
||||
public:
|
||||
enum eStatus {
|
||||
@@ -48,6 +50,11 @@ public:
|
||||
}
|
||||
void SetMutex(Mutex *mutex);
|
||||
|
||||
// only safe on connections shared with other threads if results buffered
|
||||
// unsafe to use off main thread due to internal server logging
|
||||
// throws std::runtime_error on failure
|
||||
mysql::PreparedStmt Prepare(std::string query);
|
||||
|
||||
protected:
|
||||
bool Open(
|
||||
const char *iHost,
|
||||
|
||||
+25
-204
@@ -80,39 +80,19 @@ bool Bug::IsValid(uint32 category_id)
|
||||
return bug_category_names.find(category_id) != bug_category_names.end();
|
||||
}
|
||||
|
||||
const char *EQ::constants::GetStanceName(StanceType stance_type) {
|
||||
switch (stance_type) {
|
||||
case stanceUnknown:
|
||||
return "Unknown";
|
||||
case stancePassive:
|
||||
return "Passive";
|
||||
case stanceBalanced:
|
||||
return "Balanced";
|
||||
case stanceEfficient:
|
||||
return "Efficient";
|
||||
case stanceReactive:
|
||||
return "Reactive";
|
||||
case stanceAggressive:
|
||||
return "Aggressive";
|
||||
case stanceAssist:
|
||||
return "Assist";
|
||||
case stanceBurn:
|
||||
return "Burn";
|
||||
case stanceEfficient2:
|
||||
return "Efficient2";
|
||||
case stanceBurnAE:
|
||||
return "BurnAE";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
std::string Stance::GetName(uint8 stance_id)
|
||||
{
|
||||
return IsValid(stance_id) ? stance_names[stance_id] : "UNKNOWN STANCE";
|
||||
}
|
||||
|
||||
int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
|
||||
if (EQ::ValueWithin(stance_type, EQ::constants::stancePassive, EQ::constants::stanceBurnAE)) {
|
||||
return (stance_type - EQ::constants::stancePassive);
|
||||
}
|
||||
bool Stance::IsValid(uint8 stance_id)
|
||||
{
|
||||
return stance_names.find(stance_id) != stance_names.end();
|
||||
}
|
||||
|
||||
return 0;
|
||||
uint8 Stance::GetIndex(uint8 stance_id)
|
||||
{
|
||||
return IsValid(stance_id) ? (stance_id - Stance::Passive) : 0;
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string>& EQ::constants::GetLanguageMap()
|
||||
@@ -206,102 +186,6 @@ std::string EQ::constants::GetFlyModeName(int8 flymode_id)
|
||||
return EQ::constants::GetFlyModeMap().find(flymode_id)->second;
|
||||
}
|
||||
|
||||
const std::map<bodyType, std::string>& EQ::constants::GetBodyTypeMap()
|
||||
{
|
||||
static const std::map<bodyType, std::string> bodytype_map = {
|
||||
{ BT_Humanoid, "Humanoid" },
|
||||
{ BT_Lycanthrope, "Lycanthrope" },
|
||||
{ BT_Undead, "Undead" },
|
||||
{ BT_Giant, "Giant" },
|
||||
{ BT_Construct, "Construct" },
|
||||
{ BT_Extraplanar, "Extraplanar" },
|
||||
{ BT_Magical, "Magical" },
|
||||
{ BT_SummonedUndead, "Summoned Undead" },
|
||||
{ BT_RaidGiant, "Raid Giant" },
|
||||
{ BT_RaidColdain, "Raid Coldain" },
|
||||
{ BT_NoTarget, "Untargetable" },
|
||||
{ BT_Vampire, "Vampire" },
|
||||
{ BT_Atenha_Ra, "Aten Ha Ra" },
|
||||
{ BT_Greater_Akheva, "Greater Akheva" },
|
||||
{ BT_Khati_Sha, "Khati Sha" },
|
||||
{ BT_Seru, "Seru" },
|
||||
{ BT_Grieg_Veneficus, "Grieg Veneficus" },
|
||||
{ BT_Draz_Nurakk, "Draz Nurakk" },
|
||||
{ BT_Zek, "Zek" },
|
||||
{ BT_Luggald, "Luggald" },
|
||||
{ BT_Animal, "Animal" },
|
||||
{ BT_Insect, "Insect" },
|
||||
{ BT_Monster, "Monster" },
|
||||
{ BT_Summoned, "Summoned" },
|
||||
{ BT_Plant, "Plant" },
|
||||
{ BT_Dragon, "Dragon" },
|
||||
{ BT_Summoned2, "Summoned 2" },
|
||||
{ BT_Summoned3, "Summoned 3" },
|
||||
{ BT_Dragon2, "Dragon 2" },
|
||||
{ BT_VeliousDragon, "Velious Dragon" },
|
||||
{ BT_Familiar, "Familiar" },
|
||||
{ BT_Dragon3, "Dragon 3" },
|
||||
{ BT_Boxes, "Boxes" },
|
||||
{ BT_Muramite, "Muramite" },
|
||||
{ BT_NoTarget2, "Untargetable 2" },
|
||||
{ BT_SwarmPet, "Swarm Pet" },
|
||||
{ BT_MonsterSummon, "Monster Summon" },
|
||||
{ BT_InvisMan, "Invisible Man" },
|
||||
{ BT_Special, "Special" },
|
||||
};
|
||||
|
||||
return bodytype_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id)
|
||||
{
|
||||
if (EQ::constants::GetBodyTypeMap().find(bodytype_id) != EQ::constants::GetBodyTypeMap().end()) {
|
||||
return EQ::constants::GetBodyTypeMap().find(bodytype_id)->second;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string>& EQ::constants::GetAccountStatusMap()
|
||||
{
|
||||
static const std::map<uint8, std::string> account_status_map = {
|
||||
{ AccountStatus::Player, "Player" },
|
||||
{ AccountStatus::Steward, "Steward" },
|
||||
{ AccountStatus::ApprenticeGuide, "Apprentice Guide" },
|
||||
{ AccountStatus::Guide, "Guide" },
|
||||
{ AccountStatus::QuestTroupe, "Quest Troupe" },
|
||||
{ AccountStatus::SeniorGuide, "Senior Guide" },
|
||||
{ AccountStatus::GMTester, "GM Tester" },
|
||||
{ AccountStatus::EQSupport, "EQ Support" },
|
||||
{ AccountStatus::GMStaff, "GM Staff" },
|
||||
{ AccountStatus::GMAdmin, "GM Admin" },
|
||||
{ AccountStatus::GMLeadAdmin, "GM Lead Admin" },
|
||||
{ AccountStatus::QuestMaster, "Quest Master" },
|
||||
{ AccountStatus::GMAreas, "GM Areas" },
|
||||
{ AccountStatus::GMCoder, "GM Coder" },
|
||||
{ AccountStatus::GMMgmt, "GM Mgmt" },
|
||||
{ AccountStatus::GMImpossible, "GM Impossible" },
|
||||
{ AccountStatus::Max, "GM Max" }
|
||||
};
|
||||
|
||||
return account_status_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetAccountStatusName(uint8 account_status)
|
||||
{
|
||||
for (
|
||||
auto status_level = EQ::constants::GetAccountStatusMap().rbegin();
|
||||
status_level != EQ::constants::GetAccountStatusMap().rend();
|
||||
++status_level
|
||||
) {
|
||||
if (account_status >= status_level->first) {
|
||||
return status_level->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string>& EQ::constants::GetConsiderLevelMap()
|
||||
{
|
||||
static const std::map<uint8, std::string> consider_level_map = {
|
||||
@@ -392,84 +276,6 @@ std::string EQ::constants::GetSpawnAnimationName(uint8 animation_id)
|
||||
return EQ::constants::GetSpawnAnimationMap().find(animation_id)->second;
|
||||
}
|
||||
|
||||
const std::map<int, std::string>& EQ::constants::GetObjectTypeMap()
|
||||
{
|
||||
static const std::map<int, std::string> object_type_map = {
|
||||
{ ObjectTypes::SmallBag, "Small Bag" },
|
||||
{ ObjectTypes::LargeBag, "Large Bag" },
|
||||
{ ObjectTypes::Quiver, "Quiver" },
|
||||
{ ObjectTypes::BeltPouch, "Belt Pouch" },
|
||||
{ ObjectTypes::WristPouch, "Wrist Pouch" },
|
||||
{ ObjectTypes::Backpack, "Backpack" },
|
||||
{ ObjectTypes::SmallChest, "Small Chest" },
|
||||
{ ObjectTypes::LargeChest, "Large Chest" },
|
||||
{ ObjectTypes::Bandolier, "Bandolier" },
|
||||
{ ObjectTypes::Medicine, "Medicine" },
|
||||
{ ObjectTypes::Tinkering, "Tinkering" },
|
||||
{ ObjectTypes::Lexicon, "Lexicon" },
|
||||
{ ObjectTypes::PoisonMaking, "Mortar and Pestle" },
|
||||
{ ObjectTypes::Quest, "Quest" },
|
||||
{ ObjectTypes::MixingBowl, "Mixing Bowl" },
|
||||
{ ObjectTypes::Baking, "Baking" },
|
||||
{ ObjectTypes::Tailoring, "Tailoring" },
|
||||
{ ObjectTypes::Blacksmithing, "Blacksmithing" },
|
||||
{ ObjectTypes::Fletching, "Fletching" },
|
||||
{ ObjectTypes::Brewing, "Brewing" },
|
||||
{ ObjectTypes::JewelryMaking, "Jewelry Making" },
|
||||
{ ObjectTypes::Pottery, "Pottery" },
|
||||
{ ObjectTypes::Kiln, "Kiln" },
|
||||
{ ObjectTypes::KeyMaker, "Key Maker" },
|
||||
{ ObjectTypes::ResearchWIZ, "Lexicon" },
|
||||
{ ObjectTypes::ResearchMAG, "Lexicon" },
|
||||
{ ObjectTypes::ResearchNEC, "Lexicon" },
|
||||
{ ObjectTypes::ResearchENC, "Lexicon" },
|
||||
{ ObjectTypes::Unknown, "Unknown" },
|
||||
{ ObjectTypes::ResearchPractice, "Lexicon" },
|
||||
{ ObjectTypes::Alchemy, "Alchemy" },
|
||||
{ ObjectTypes::HighElfForge, "High Elf Forge" },
|
||||
{ ObjectTypes::DarkElfForge, "Dark Elf Forge" },
|
||||
{ ObjectTypes::OgreForge, "Ogre Forge" },
|
||||
{ ObjectTypes::DwarfForge, "Dwarf Forge" },
|
||||
{ ObjectTypes::GnomeForge, "Gnome Forge" },
|
||||
{ ObjectTypes::BarbarianForge, "Barbarian Forge" },
|
||||
{ ObjectTypes::IksarForge, "Iksar Forge" },
|
||||
{ ObjectTypes::HumanForgeOne, "Human Forge" },
|
||||
{ ObjectTypes::HumanForgeTwo, "Human Forge" },
|
||||
{ ObjectTypes::HalflingTailoringOne, "Halfling Tailoring" },
|
||||
{ ObjectTypes::HalflingTailoringTwo, "Halfling Tailoring" },
|
||||
{ ObjectTypes::EruditeTailoring, "Erudite Tailoring" },
|
||||
{ ObjectTypes::WoodElfTailoring, "Wood Elf Tailoring" },
|
||||
{ ObjectTypes::WoodElfFletching, "Wood Elf Fletching" },
|
||||
{ ObjectTypes::IksarPottery, "Iksar Pottery" },
|
||||
{ ObjectTypes::Fishing, "Fishing" },
|
||||
{ ObjectTypes::TrollForge, "Troll Forge" },
|
||||
{ ObjectTypes::WoodElfForge, "Wood Elf Forge" },
|
||||
{ ObjectTypes::HalflingForge, "Halfling Forge" },
|
||||
{ ObjectTypes::EruditeForge, "Erudite Forge" },
|
||||
{ ObjectTypes::Merchant, "Merchant" },
|
||||
{ ObjectTypes::FroglokForge, "Froglok Forge" },
|
||||
{ ObjectTypes::Augmenter, "Augmenter" },
|
||||
{ ObjectTypes::Churn, "Churn" },
|
||||
{ ObjectTypes::TransformationMold, "Transformation Mold" },
|
||||
{ ObjectTypes::DetransformationMold, "Detransformation Mold" },
|
||||
{ ObjectTypes::Unattuner, "Unattuner" },
|
||||
{ ObjectTypes::TradeskillBag, "Tradeskill Bag" },
|
||||
{ ObjectTypes::CollectibleBag, "Collectible Bag" },
|
||||
{ ObjectTypes::NoDeposit, "No Deposit" }
|
||||
};
|
||||
|
||||
return object_type_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetObjectTypeName(int object_type)
|
||||
{
|
||||
if (!EQ::ValueWithin(object_type, ObjectTypes::SmallBag, ObjectTypes::NoDeposit)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return EQ::constants::GetObjectTypeMap().find(object_type)->second;
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string> &EQ::constants::GetWeatherTypeMap()
|
||||
{
|
||||
static const std::map<uint8, std::string> weather_type_map = {
|
||||
@@ -629,6 +435,21 @@ std::string EQ::constants::GetConsiderColorName(uint32 consider_color)
|
||||
return c != EQ::constants::GetConsiderColorMap().end() ? c->second : std::string();
|
||||
}
|
||||
|
||||
std::string AccountStatus::GetName(uint8 account_status)
|
||||
{
|
||||
for (
|
||||
auto e = account_status_names.rbegin();
|
||||
e != account_status_names.rend();
|
||||
++e
|
||||
) {
|
||||
if (account_status >= e->first) {
|
||||
return e->second;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN ACCOUNT STATUS";
|
||||
}
|
||||
|
||||
std::string ComparisonType::GetName(uint8 type)
|
||||
{
|
||||
return IsValid(type) ? comparison_types[type] : "UNKNOWN COMPARISON TYPE";
|
||||
|
||||
+119
-148
@@ -26,6 +26,48 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace AccountStatus {
|
||||
constexpr uint8 Player = 0;
|
||||
constexpr uint8 Steward = 10;
|
||||
constexpr uint8 ApprenticeGuide = 20;
|
||||
constexpr uint8 Guide = 50;
|
||||
constexpr uint8 QuestTroupe = 80;
|
||||
constexpr uint8 SeniorGuide = 81;
|
||||
constexpr uint8 GMTester = 85;
|
||||
constexpr uint8 EQSupport = 90;
|
||||
constexpr uint8 GMStaff = 95;
|
||||
constexpr uint8 GMAdmin = 100;
|
||||
constexpr uint8 GMLeadAdmin = 150;
|
||||
constexpr uint8 QuestMaster = 160;
|
||||
constexpr uint8 GMAreas = 170;
|
||||
constexpr uint8 GMCoder = 180;
|
||||
constexpr uint8 GMMgmt = 200;
|
||||
constexpr uint8 GMImpossible = 250;
|
||||
constexpr uint8 Max = 255;
|
||||
|
||||
std::string GetName(uint8 account_status);
|
||||
}
|
||||
|
||||
static std::map<uint8, std::string> account_status_names = {
|
||||
{ AccountStatus::Player, "Player" },
|
||||
{ AccountStatus::Steward, "Steward" },
|
||||
{ AccountStatus::ApprenticeGuide, "Apprentice Guide" },
|
||||
{ AccountStatus::Guide, "Guide" },
|
||||
{ AccountStatus::QuestTroupe, "Quest Troupe" },
|
||||
{ AccountStatus::SeniorGuide, "Senior Guide" },
|
||||
{ AccountStatus::GMTester, "GM Tester" },
|
||||
{ AccountStatus::EQSupport, "EQ Support" },
|
||||
{ AccountStatus::GMStaff, "GM Staff" },
|
||||
{ AccountStatus::GMAdmin, "GM Admin" },
|
||||
{ AccountStatus::GMLeadAdmin, "GM Lead Admin" },
|
||||
{ AccountStatus::QuestMaster, "Quest Master" },
|
||||
{ AccountStatus::GMAreas, "GM Areas" },
|
||||
{ AccountStatus::GMCoder, "GM Coder" },
|
||||
{ AccountStatus::GMMgmt, "GM Mgmt" },
|
||||
{ AccountStatus::GMImpossible, "GM Impossible" },
|
||||
{ AccountStatus::Max, "GM Max" }
|
||||
};
|
||||
|
||||
namespace ComparisonType {
|
||||
constexpr uint8 Equal = 0;
|
||||
constexpr uint8 NotEqual = 1;
|
||||
@@ -55,7 +97,6 @@ static std::map<uint8, std::string> comparison_types = {
|
||||
{ ComparisonType::NotBetween, "Not Between" },
|
||||
};
|
||||
|
||||
|
||||
// local definitions are the result of using hybrid-client or server-only values and methods
|
||||
namespace EQ
|
||||
{
|
||||
@@ -91,7 +132,7 @@ namespace EQ
|
||||
using RoF2::invtype::KRONO_SIZE;
|
||||
using RoF2::invtype::OTHER_SIZE;
|
||||
|
||||
using Titanium::invtype::TRADE_NPC_SIZE;
|
||||
using RoF2::invtype::TRADE_NPC_SIZE;
|
||||
|
||||
using RoF2::invtype::TYPE_INVALID;
|
||||
using RoF2::invtype::TYPE_BEGIN;
|
||||
@@ -118,7 +159,7 @@ namespace EQ
|
||||
using RoF2::invslot::SLOT_INVALID;
|
||||
using RoF2::invslot::SLOT_BEGIN;
|
||||
|
||||
using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE;
|
||||
using RoF2::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE;
|
||||
|
||||
const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value)
|
||||
|
||||
@@ -138,28 +179,28 @@ namespace EQ
|
||||
using RoF2::invslot::BONUS_STAT_END;
|
||||
using RoF2::invslot::BONUS_SKILL_END;
|
||||
|
||||
using Titanium::invslot::BANK_BEGIN;
|
||||
using SoF::invslot::BANK_END;
|
||||
using RoF2::invslot::BANK_BEGIN;
|
||||
using RoF2::invslot::BANK_END;
|
||||
|
||||
using Titanium::invslot::SHARED_BANK_BEGIN;
|
||||
using Titanium::invslot::SHARED_BANK_END;
|
||||
using RoF2::invslot::SHARED_BANK_BEGIN;
|
||||
using RoF2::invslot::SHARED_BANK_END;
|
||||
|
||||
using Titanium::invslot::TRADE_BEGIN;
|
||||
using Titanium::invslot::TRADE_END;
|
||||
using RoF2::invslot::TRADE_BEGIN;
|
||||
using RoF2::invslot::TRADE_END;
|
||||
|
||||
using Titanium::invslot::TRADE_NPC_END;
|
||||
using RoF2::invslot::TRADE_NPC_END;
|
||||
|
||||
using Titanium::invslot::WORLD_BEGIN;
|
||||
using Titanium::invslot::WORLD_END;
|
||||
using RoF2::invslot::WORLD_BEGIN;
|
||||
using RoF2::invslot::WORLD_END;
|
||||
|
||||
using Titanium::invslot::TRIBUTE_BEGIN;
|
||||
using Titanium::invslot::TRIBUTE_END;
|
||||
using RoF2::invslot::TRIBUTE_BEGIN;
|
||||
using RoF2::invslot::TRIBUTE_END;
|
||||
|
||||
using Titanium::invslot::GUILD_TRIBUTE_BEGIN;
|
||||
using Titanium::invslot::GUILD_TRIBUTE_END;
|
||||
using RoF2::invslot::GUILD_TRIBUTE_BEGIN;
|
||||
using RoF2::invslot::GUILD_TRIBUTE_END;
|
||||
|
||||
const int16 CORPSE_BEGIN = invslot::slotGeneral1;
|
||||
const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor;
|
||||
const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor;
|
||||
|
||||
using RoF2::invslot::EQUIPMENT_BITMASK;
|
||||
using RoF2::invslot::GENERAL_BITMASK;
|
||||
@@ -173,38 +214,40 @@ namespace EQ
|
||||
} // namespace invslot
|
||||
|
||||
namespace invbag {
|
||||
using Titanium::invbag::SLOT_INVALID;
|
||||
using Titanium::invbag::SLOT_BEGIN;
|
||||
using Titanium::invbag::SLOT_END;
|
||||
using Titanium::invbag::SLOT_COUNT;
|
||||
using RoF2::invbag::SLOT_INVALID;
|
||||
using RoF2::invbag::SLOT_BEGIN;
|
||||
using RoF2::invbag::SLOT_END;
|
||||
using RoF2::invbag::SLOT_COUNT;
|
||||
|
||||
using Titanium::invbag::GENERAL_BAGS_BEGIN;
|
||||
using RoF2::invslot::WORLD_END;
|
||||
|
||||
const int16 GENERAL_BAGS_BEGIN = WORLD_END + 1;
|
||||
const int16 GENERAL_BAGS_COUNT = invslot::GENERAL_COUNT * SLOT_COUNT;
|
||||
const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1;
|
||||
const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1;
|
||||
|
||||
const int16 GENERAL_BAGS_8_COUNT = 8 * SLOT_COUNT;
|
||||
const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1;
|
||||
const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1;
|
||||
|
||||
const int16 CURSOR_BAG_BEGIN = 351;
|
||||
const int16 CURSOR_BAG_BEGIN = GENERAL_BAGS_END + 1;
|
||||
const int16 CURSOR_BAG_COUNT = SLOT_COUNT;
|
||||
const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1;
|
||||
const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1;
|
||||
|
||||
using Titanium::invbag::BANK_BAGS_BEGIN;
|
||||
const int16 BANK_BAGS_BEGIN = CURSOR_BAG_END + 1;
|
||||
const int16 BANK_BAGS_COUNT = (invtype::BANK_SIZE * SLOT_COUNT);
|
||||
const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1;
|
||||
const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1;
|
||||
|
||||
const int16 BANK_BAGS_16_COUNT = 16 * SLOT_COUNT;
|
||||
const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1;
|
||||
const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1;
|
||||
|
||||
using Titanium::invbag::SHARED_BANK_BAGS_BEGIN;
|
||||
const int16 SHARED_BANK_BAGS_BEGIN = BANK_BAGS_END + 1;
|
||||
const int16 SHARED_BANK_BAGS_COUNT = invtype::SHARED_BANK_SIZE * SLOT_COUNT;
|
||||
const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1;
|
||||
const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1;
|
||||
|
||||
using Titanium::invbag::TRADE_BAGS_BEGIN;
|
||||
const int16 TRADE_BAGS_BEGIN = SHARED_BANK_BAGS_END + 1;
|
||||
const int16 TRADE_BAGS_COUNT = invtype::TRADE_SIZE * SLOT_COUNT;
|
||||
const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1;
|
||||
const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1;
|
||||
|
||||
using Titanium::invbag::GetInvBagIndexName;
|
||||
using RoF2::invbag::GetInvBagIndexName;
|
||||
|
||||
} // namespace invbag
|
||||
|
||||
@@ -233,19 +276,6 @@ namespace EQ
|
||||
const size_t SAY_LINK_CLOSER_SIZE = 1;
|
||||
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
|
||||
|
||||
enum StanceType : int {
|
||||
stanceUnknown = 0,
|
||||
stancePassive,
|
||||
stanceBalanced,
|
||||
stanceEfficient,
|
||||
stanceReactive,
|
||||
stanceAggressive,
|
||||
stanceAssist,
|
||||
stanceBurn,
|
||||
stanceEfficient2,
|
||||
stanceBurnAE
|
||||
};
|
||||
|
||||
enum BotSpellIDs : int {
|
||||
Warrior = 3001,
|
||||
Cleric,
|
||||
@@ -296,70 +326,6 @@ namespace EQ
|
||||
Looting
|
||||
};
|
||||
|
||||
enum ObjectTypes : int {
|
||||
SmallBag,
|
||||
LargeBag,
|
||||
Quiver,
|
||||
BeltPouch,
|
||||
WristPouch,
|
||||
Backpack,
|
||||
SmallChest,
|
||||
LargeChest,
|
||||
Bandolier,
|
||||
Medicine,
|
||||
Tinkering,
|
||||
Lexicon,
|
||||
PoisonMaking,
|
||||
Quest,
|
||||
MixingBowl,
|
||||
Baking,
|
||||
Tailoring,
|
||||
Blacksmithing,
|
||||
Fletching,
|
||||
Brewing,
|
||||
JewelryMaking,
|
||||
Pottery,
|
||||
Kiln,
|
||||
KeyMaker,
|
||||
ResearchWIZ,
|
||||
ResearchMAG,
|
||||
ResearchNEC,
|
||||
ResearchENC,
|
||||
Unknown,
|
||||
ResearchPractice,
|
||||
Alchemy,
|
||||
HighElfForge,
|
||||
DarkElfForge,
|
||||
OgreForge,
|
||||
DwarfForge,
|
||||
GnomeForge,
|
||||
BarbarianForge,
|
||||
IksarForge,
|
||||
HumanForgeOne,
|
||||
HumanForgeTwo,
|
||||
HalflingTailoringOne,
|
||||
HalflingTailoringTwo,
|
||||
EruditeTailoring,
|
||||
WoodElfTailoring,
|
||||
WoodElfFletching,
|
||||
IksarPottery,
|
||||
Fishing,
|
||||
TrollForge,
|
||||
WoodElfForge,
|
||||
HalflingForge,
|
||||
EruditeForge,
|
||||
Merchant,
|
||||
FroglokForge,
|
||||
Augmenter,
|
||||
Churn,
|
||||
TransformationMold,
|
||||
DetransformationMold,
|
||||
Unattuner,
|
||||
TradeskillBag,
|
||||
CollectibleBag,
|
||||
NoDeposit
|
||||
};
|
||||
|
||||
enum WeatherTypes : uint8 {
|
||||
None,
|
||||
Raining,
|
||||
@@ -385,9 +351,6 @@ namespace EQ
|
||||
Proximity
|
||||
};
|
||||
|
||||
const char *GetStanceName(StanceType stance_type);
|
||||
int ConvertStanceTypeToIndex(StanceType stance_type);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetLanguageMap();
|
||||
std::string GetLanguageName(uint8 language_id);
|
||||
|
||||
@@ -397,12 +360,6 @@ namespace EQ
|
||||
extern const std::map<int8, std::string>& GetFlyModeMap();
|
||||
std::string GetFlyModeName(int8 flymode_id);
|
||||
|
||||
extern const std::map<bodyType, std::string>& GetBodyTypeMap();
|
||||
std::string GetBodyTypeName(bodyType bodytype_id);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetAccountStatusMap();
|
||||
std::string GetAccountStatusName(uint8 account_status);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetConsiderLevelMap();
|
||||
std::string GetConsiderLevelName(uint8 consider_level);
|
||||
|
||||
@@ -415,9 +372,6 @@ namespace EQ
|
||||
extern const std::map<uint8, std::string>& GetSpawnAnimationMap();
|
||||
std::string GetSpawnAnimationName(uint8 animation_id);
|
||||
|
||||
extern const std::map<int, std::string>& GetObjectTypeMap();
|
||||
std::string GetObjectTypeName(int object_type);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetWeatherTypeMap();
|
||||
std::string GetWeatherTypeName(uint8 weather_type);
|
||||
|
||||
@@ -433,10 +387,6 @@ namespace EQ
|
||||
extern const std::map<uint32, std::string>& GetConsiderColorMap();
|
||||
std::string GetConsiderColorName(uint32 consider_color);
|
||||
|
||||
const int STANCE_TYPE_FIRST = stancePassive;
|
||||
const int STANCE_TYPE_LAST = stanceBurnAE;
|
||||
const int STANCE_TYPE_COUNT = stanceBurnAE;
|
||||
|
||||
} /*constants*/
|
||||
|
||||
namespace profile {
|
||||
@@ -503,7 +453,7 @@ namespace EQ
|
||||
Raid,
|
||||
Guild
|
||||
};
|
||||
}; // namespace consent
|
||||
};
|
||||
} /*EQEmu*/
|
||||
|
||||
enum ServerLockType : int {
|
||||
@@ -512,26 +462,6 @@ enum ServerLockType : int {
|
||||
Unlock
|
||||
};
|
||||
|
||||
enum AccountStatus : uint8 {
|
||||
Player = 0,
|
||||
Steward = 10,
|
||||
ApprenticeGuide = 20,
|
||||
Guide = 50,
|
||||
QuestTroupe = 80,
|
||||
SeniorGuide = 81,
|
||||
GMTester = 85,
|
||||
EQSupport = 90,
|
||||
GMStaff = 95,
|
||||
GMAdmin = 100,
|
||||
GMLeadAdmin = 150,
|
||||
QuestMaster = 160,
|
||||
GMAreas = 170,
|
||||
GMCoder = 180,
|
||||
GMMgmt = 200,
|
||||
GMImpossible = 250,
|
||||
Max = 255
|
||||
};
|
||||
|
||||
enum Invisibility : uint8 {
|
||||
Visible,
|
||||
Invisible,
|
||||
@@ -793,4 +723,45 @@ static std::map<uint32, std::string> bug_category_names = {
|
||||
{ Bug::Category::Mercenaries, "Mercenaries" }
|
||||
};
|
||||
|
||||
namespace Stance {
|
||||
constexpr uint32 Unknown = 0;
|
||||
constexpr uint32 Passive = 1;
|
||||
constexpr uint32 Balanced = 2;
|
||||
constexpr uint32 Efficient = 3;
|
||||
constexpr uint32 Reactive = 4;
|
||||
constexpr uint32 Aggressive = 5;
|
||||
constexpr uint32 Assist = 6;
|
||||
constexpr uint32 Burn = 7;
|
||||
constexpr uint32 Efficient2 = 8;
|
||||
constexpr uint32 AEBurn = 9;
|
||||
|
||||
std::string GetName(uint8 stance_id);
|
||||
uint8 GetIndex(uint8 stance_id);
|
||||
bool IsValid(uint8 stance_id);
|
||||
}
|
||||
|
||||
static std::map<uint32, std::string> stance_names = {
|
||||
{ Stance::Unknown, "Unknown" },
|
||||
{ Stance::Passive, "Passive" },
|
||||
{ Stance::Balanced, "Balanced" },
|
||||
{ Stance::Efficient, "Efficient" },
|
||||
{ Stance::Reactive, "Reactive" },
|
||||
{ Stance::Aggressive, "Aggressive" },
|
||||
{ Stance::Assist, "Assist" },
|
||||
{ Stance::Burn, "Burn" },
|
||||
{ Stance::Efficient2, "Efficient" },
|
||||
{ Stance::AEBurn, "AE Burn" }
|
||||
};
|
||||
|
||||
namespace PCNPCOnlyFlagType {
|
||||
constexpr int PC = 1;
|
||||
constexpr int NPC = 2;
|
||||
}
|
||||
|
||||
namespace BookType {
|
||||
constexpr uint8 Scroll = 0;
|
||||
constexpr uint8 Book = 1;
|
||||
constexpr uint8 ItemInfo = 2;
|
||||
}
|
||||
|
||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||
|
||||
@@ -67,6 +67,7 @@ N(OP_Buff),
|
||||
N(OP_BuffCreate),
|
||||
N(OP_BuffRemoveRequest),
|
||||
N(OP_Bug),
|
||||
N(OP_BuyerItems),
|
||||
N(OP_CameraEffect),
|
||||
N(OP_Camp),
|
||||
N(OP_CancelSneakHide),
|
||||
@@ -533,6 +534,7 @@ N(OP_Stamina),
|
||||
N(OP_Stun),
|
||||
N(OP_Surname),
|
||||
N(OP_SwapSpell),
|
||||
N(OP_SystemFingerprint),
|
||||
N(OP_TargetBuffs),
|
||||
N(OP_TargetCommand),
|
||||
N(OP_TargetHoTT),
|
||||
|
||||
@@ -758,10 +758,10 @@ typedef enum {
|
||||
FilterFocusEffects = 22, //0=show, 1=hide
|
||||
FilterPetSpells = 23, //0=show, 1=hide
|
||||
FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide
|
||||
FilterUnknown25 = 25,
|
||||
FilterUnknown26 = 26,
|
||||
FilterUnknown27 = 27,
|
||||
FilterUnknown28 = 28,
|
||||
FilterItemSpeech = 25, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
|
||||
_FilterCount
|
||||
} eqFilterType;
|
||||
|
||||
@@ -1129,4 +1129,7 @@ enum ExpSource
|
||||
#define PARCEL_LIMIT 5
|
||||
#define PARCEL_BEGIN_SLOT 1
|
||||
|
||||
namespace DoorType {
|
||||
constexpr uint32 BuyerStall = 155;
|
||||
}
|
||||
#endif /*COMMON_EQ_CONSTANTS_H*/
|
||||
|
||||
+386
-66
@@ -323,6 +323,7 @@ union
|
||||
bool show_name;
|
||||
bool guild_show;
|
||||
bool trader;
|
||||
bool buyer;
|
||||
};
|
||||
|
||||
struct PlayerState_Struct {
|
||||
@@ -1620,6 +1621,32 @@ struct MoveItem_Struct
|
||||
/*0012*/
|
||||
};
|
||||
|
||||
// New for RoF2 - Size: 12
|
||||
struct InventorySlot_Struct
|
||||
{
|
||||
/*000*/ int16 Type; // Worn and Normal inventory = 0, Bank = 1, Shared Bank = 2, Delete Item = -1
|
||||
/*002*/ int16 Unknown02;
|
||||
/*004*/ int16 Slot;
|
||||
/*006*/ int16 SubIndex;
|
||||
/*008*/ int16 AugIndex; // Guessing - Seen 0xffff
|
||||
/*010*/ int16 Unknown01; // Normally 0 - Seen 13262 when deleting an item, but didn't match item ID
|
||||
/*012*/
|
||||
};
|
||||
|
||||
struct MultiMoveItemSub_Struct
|
||||
{
|
||||
/*0000*/ InventorySlot_Struct from_slot;
|
||||
/*0012*/ InventorySlot_Struct to_slot;
|
||||
/*0024*/ uint32 number_in_stack;
|
||||
/*0028*/ uint8 unknown[8];
|
||||
};
|
||||
|
||||
struct MultiMoveItem_Struct
|
||||
{
|
||||
/*0000*/ uint32 count;
|
||||
/*0004*/ MultiMoveItemSub_Struct moves[0];
|
||||
};
|
||||
|
||||
// both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will
|
||||
// be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this
|
||||
// info at the moment..but, it is forwarded on to the server for handling/future use)
|
||||
@@ -3113,60 +3140,370 @@ struct BazaarSearchResults_Struct {
|
||||
// Barter/Buyer
|
||||
//
|
||||
//
|
||||
enum {
|
||||
Barter_BuyerSearch = 0,
|
||||
Barter_SellerSearch = 1,
|
||||
Barter_BuyerModeOn = 2,
|
||||
Barter_BuyerModeOff = 3,
|
||||
Barter_BuyerItemUpdate = 5,
|
||||
Barter_BuyerItemRemove = 6,
|
||||
Barter_SellItem = 7,
|
||||
#define MAX_BUYER_COMPENSATION_ITEMS 10
|
||||
|
||||
enum BarterBuyerActions {
|
||||
Barter_BuyerSearch = 0,
|
||||
Barter_SellerSearch = 1,
|
||||
Barter_BuyerModeOn = 2,
|
||||
Barter_BuyerModeOff = 3,
|
||||
Barter_BuyerItemStart = 4,
|
||||
Barter_BuyerItemUpdate = 5,
|
||||
Barter_BuyerItemRemove = 6,
|
||||
Barter_SellItem = 7,
|
||||
Barter_SellerTransactionComplete = 8,
|
||||
Barter_BuyerTransactionComplete = 9,
|
||||
Barter_BuyerInspectBegin = 10,
|
||||
Barter_BuyerInspectEnd = 11,
|
||||
Barter_BuyerAppearance = 12,
|
||||
Barter_BuyerInspectWindow = 13,
|
||||
Barter_BarterItemInspect = 14,
|
||||
Barter_SellerBrowsing = 15,
|
||||
Barter_BuyerSearchResults = 16,
|
||||
Barter_Welcome = 17,
|
||||
Barter_WelcomeMessageUpdate = 19,
|
||||
Barter_BuyerItemInspect = 21,
|
||||
Barter_Unknown23 = 23
|
||||
Barter_BuyerTransactionComplete = 9,
|
||||
Barter_BuyerInspectBegin = 10,
|
||||
Barter_BuyerInspectEnd = 11,
|
||||
Barter_BuyerAppearance = 12,
|
||||
Barter_BuyerInspectWindow = 13,
|
||||
Barter_BarterItemInspect = 14,
|
||||
Barter_SellerBrowsing = 15,
|
||||
Barter_BuyerSearchResults = 16,
|
||||
Barter_Welcome = 17,
|
||||
Barter_WelcomeMessageUpdate = 19,
|
||||
Barter_Greeting = 20,
|
||||
Barter_BuyerItemInspect = 21,
|
||||
Barter_OpenBarterWindow = 23,
|
||||
Barter_AddToBarterWindow = 26,
|
||||
Barter_RemoveFromBarterWindow = 27,
|
||||
Barter_RemoveFromMerchantWindow = 50, //Not a client item. Used for internal communications.
|
||||
Barter_FailedTransaction = 51,
|
||||
Barter_BuyerCouldNotBeFound = 52,
|
||||
Barter_FailedBuyerChecks = 53,
|
||||
Barter_SellerCouldNotBeFound = 54,
|
||||
Barter_FailedSellerChecks = 55
|
||||
};
|
||||
|
||||
enum BarterBuyerSubActions {
|
||||
Barter_Success = 0,
|
||||
Barter_Failure = 1,
|
||||
Barter_DataOutOfDate = 4,
|
||||
Barter_SellerDoesNotHaveItem = 6,
|
||||
Barter_SameZone = 8
|
||||
};
|
||||
|
||||
enum BuyerBarter {
|
||||
Off = 0,
|
||||
On = 1
|
||||
};
|
||||
|
||||
struct BuyerRemoveItem_Struct {
|
||||
uint32 action;
|
||||
uint32 buy_slot_id;
|
||||
};
|
||||
|
||||
struct BuyerRemoveItemFromMerchantWindow_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 buy_slot_id;
|
||||
uint32 unknown_012;
|
||||
};
|
||||
|
||||
struct BuyerGeneric_Struct {
|
||||
uint32 action;
|
||||
char payload[];
|
||||
};
|
||||
|
||||
struct BuyerMessaging_Struct {
|
||||
uint32 action;
|
||||
uint32 sub_action;
|
||||
uint32 zone_id;
|
||||
uint32 buyer_id;
|
||||
uint32 buyer_entity_id;
|
||||
char buyer_name[64];
|
||||
uint32 buy_item_id;
|
||||
uint32 buy_item_qty;
|
||||
uint64 buy_item_cost;
|
||||
uint32 buy_item_icon;
|
||||
uint32 seller_entity_id;
|
||||
char seller_name[64];
|
||||
char item_name[64];
|
||||
uint32 slot;
|
||||
uint32 seller_quantity;
|
||||
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
|
||||
};
|
||||
|
||||
struct BuyerAddBuyertoBarterWindow_Struct {
|
||||
uint32 action;
|
||||
uint32 zone_id;
|
||||
uint32 buyer_id;
|
||||
uint32 buyer_entity_id;
|
||||
char buyer_name[64];
|
||||
};
|
||||
|
||||
struct BuyerRemoveBuyerFromBarterWindow_Struct {
|
||||
uint32 action;
|
||||
uint32 buyer_id;
|
||||
};
|
||||
|
||||
struct BuyerBrowsing_Struct {
|
||||
uint32 action;
|
||||
char char_name[64];
|
||||
};
|
||||
|
||||
struct BuyerGreeting_Struct {
|
||||
uint32 action;
|
||||
uint32 buyer_id;
|
||||
};
|
||||
|
||||
struct BuyerWelcomeMessageUpdate_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ char WelcomeMessage[256];
|
||||
uint32 action;
|
||||
char welcome_message[256];
|
||||
};
|
||||
|
||||
struct BuyerItemSearch_Struct {
|
||||
/*000*/ uint32 Unknown000;
|
||||
/*004*/ char SearchString[64];
|
||||
struct BuyerLineTradeItems_Struct {
|
||||
uint32 item_id;
|
||||
uint32 item_quantity;
|
||||
uint32 item_icon;
|
||||
std::string item_name;
|
||||
|
||||
void operator*=(uint32 multiplier)
|
||||
{
|
||||
this->item_quantity *= multiplier;
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_quantity),
|
||||
CEREAL_NVP(item_icon),
|
||||
CEREAL_NVP(item_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BuyerItemSearchResultEntry_Struct {
|
||||
/*000*/ char ItemName[64];
|
||||
/*064*/ uint32 ItemID;
|
||||
/*068*/ uint32 Unknown068;
|
||||
/*072*/ uint32 Unknown072;
|
||||
struct BuyerLineItems_Struct {
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
uint32 item_icon;
|
||||
uint32 item_quantity;
|
||||
uint8 item_toggle;
|
||||
uint32 item_cost;
|
||||
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(slot),
|
||||
CEREAL_NVP(enabled),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(item_icon),
|
||||
CEREAL_NVP(item_quantity),
|
||||
CEREAL_NVP(item_toggle),
|
||||
CEREAL_NVP(item_cost),
|
||||
CEREAL_NVP(trade_items)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#define MAX_BUYER_ITEMSEARCH_RESULTS 200
|
||||
struct BuyerBuyLines_Struct {
|
||||
uint32 action;
|
||||
union {
|
||||
uint32 no_items;
|
||||
uint32 string_length;
|
||||
};
|
||||
std::vector<BuyerLineItems_Struct> buy_lines;
|
||||
|
||||
struct BuyerItemSearchResults_Struct {
|
||||
uint32 Action;
|
||||
uint32 ResultCount;
|
||||
BuyerItemSearchResultEntry_Struct Results[MAX_BUYER_ITEMSEARCH_RESULTS];
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(action),
|
||||
CEREAL_NVP(no_items),
|
||||
CEREAL_NVP(buy_lines)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BuyerLineSellItem_Struct {
|
||||
uint32 action;
|
||||
uint32 sub_action;
|
||||
uint32 error_code;
|
||||
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
|
||||
uint32 buyer_entity_id;
|
||||
uint32 buyer_id;
|
||||
std::string buyer_name;
|
||||
uint32 seller_entity_id;
|
||||
std::string seller_name;
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
char item_name[64];
|
||||
uint32 item_icon;
|
||||
uint32 item_quantity;
|
||||
uint8 item_toggle;
|
||||
uint32 item_cost;
|
||||
uint32 no_trade_items;
|
||||
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||
uint32 seller_quantity;
|
||||
uint32 zone_id;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(action),
|
||||
CEREAL_NVP(sub_action),
|
||||
CEREAL_NVP(error_code),
|
||||
CEREAL_NVP(purchase_method),
|
||||
CEREAL_NVP(buyer_entity_id),
|
||||
CEREAL_NVP(buyer_id),
|
||||
CEREAL_NVP(buyer_name),
|
||||
CEREAL_NVP(seller_entity_id),
|
||||
CEREAL_NVP(seller_name),
|
||||
CEREAL_NVP(slot),
|
||||
CEREAL_NVP(enabled),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(item_icon),
|
||||
CEREAL_NVP(item_quantity),
|
||||
CEREAL_NVP(item_toggle),
|
||||
CEREAL_NVP(item_cost),
|
||||
CEREAL_NVP(no_trade_items),
|
||||
CEREAL_NVP(trade_items),
|
||||
CEREAL_NVP(seller_quantity),
|
||||
CEREAL_NVP(zone_id)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BuyerLineItemsSearch_Struct {
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
char item_name[64];
|
||||
uint32 item_icon;
|
||||
uint32 item_quantity;
|
||||
uint8 item_toggle;
|
||||
uint32 item_cost;
|
||||
uint32 buyer_id;
|
||||
uint32 buyer_entity_id;
|
||||
uint32 buyer_zone_id;
|
||||
uint32 buyer_zone_instance_id;
|
||||
std::string buyer_name;
|
||||
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(slot),
|
||||
CEREAL_NVP(enabled),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(item_icon),
|
||||
CEREAL_NVP(item_quantity),
|
||||
CEREAL_NVP(item_toggle),
|
||||
CEREAL_NVP(item_cost),
|
||||
CEREAL_NVP(buyer_id),
|
||||
CEREAL_NVP(buyer_entity_id),
|
||||
CEREAL_NVP(buyer_zone_id),
|
||||
CEREAL_NVP(buyer_zone_instance_id),
|
||||
CEREAL_NVP(buyer_name),
|
||||
CEREAL_NVP(trade_items)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BuyerLineSearch_Struct {
|
||||
uint32 action;
|
||||
uint32 no_items;
|
||||
std::string search_string;
|
||||
uint32 transaction_id;
|
||||
std::vector<BuyerLineItemsSearch_Struct> buy_line;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(action),
|
||||
CEREAL_NVP(no_items),
|
||||
CEREAL_NVP(search_string),
|
||||
CEREAL_NVP(transaction_id),
|
||||
CEREAL_NVP(buy_line)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BuyerSetAppearance_Struct {
|
||||
uint32 action;
|
||||
uint32 entity_id;
|
||||
uint32 status; // 0 off 1 on
|
||||
char buyer_name[64];
|
||||
};
|
||||
|
||||
struct BarterItemSearchLinkRequest_Struct {
|
||||
uint32 action;
|
||||
uint32 searcher_id;
|
||||
uint32 unknown_008;
|
||||
uint32 unknown_012;
|
||||
uint32 item_id;
|
||||
uint32 unknown_020;
|
||||
};
|
||||
|
||||
struct BuyerInspectRequest_Struct {
|
||||
uint32 action;
|
||||
uint32 buyer_id;
|
||||
uint32 approval;
|
||||
};
|
||||
|
||||
struct BarterSearchRequest_Struct {
|
||||
uint32 Action;
|
||||
char SearchString[64];
|
||||
uint32 SearchID;
|
||||
uint32 action;
|
||||
char search_string[64];
|
||||
uint32 transaction_id;
|
||||
uint32 unknown_072;
|
||||
uint32 buyer_id;
|
||||
uint8 search_scope; //0 All Buyers, 1 Local Buyers
|
||||
uint16 zone_id;
|
||||
};
|
||||
|
||||
struct BuyerItemSearch_Struct {
|
||||
uint32 action;
|
||||
char search_string[64];
|
||||
};
|
||||
|
||||
struct BuyerItemSearchResultEntry_Struct {
|
||||
char item_name[64];
|
||||
uint32 item_id;
|
||||
uint32 item_icon;
|
||||
uint32 unknown_072;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_icon),
|
||||
CEREAL_NVP(unknown_072)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BuyerItemSearchResults_Struct {
|
||||
uint32 action;
|
||||
uint32 result_count;
|
||||
std::vector<BuyerItemSearchResultEntry_Struct> results;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(action),
|
||||
CEREAL_NVP(result_count),
|
||||
CEREAL_NVP(results)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//old below here
|
||||
struct BuyerItemSearchLinkRequest_Struct {
|
||||
/*000*/ uint32 Action; // 0x00000015
|
||||
/*004*/ uint32 ItemID;
|
||||
@@ -3174,31 +3511,6 @@ struct BuyerItemSearchLinkRequest_Struct {
|
||||
/*012*/ uint32 Unknown012;
|
||||
};
|
||||
|
||||
struct BarterItemSearchLinkRequest_Struct {
|
||||
/*000*/ uint32 Action; // 0x0000000E
|
||||
/*004*/ uint32 SearcherID;
|
||||
/*008*/ uint32 Unknown008;
|
||||
/*012*/ uint32 Unknown012;
|
||||
/*016*/ uint32 ItemID;
|
||||
/*020*/ uint32 Unknown020;
|
||||
};
|
||||
|
||||
struct BuyerInspectRequest_Struct {
|
||||
uint32 Action;
|
||||
uint32 BuyerID;
|
||||
uint32 Approval;
|
||||
};
|
||||
|
||||
struct BuyerBrowsing_Struct {
|
||||
uint32 Action;
|
||||
char PlayerName[64];
|
||||
};
|
||||
|
||||
struct BuyerRemoveItem_Struct {
|
||||
uint32 Action;
|
||||
uint32 BuySlot;
|
||||
};
|
||||
|
||||
struct ServerSideFilters_Struct {
|
||||
uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group
|
||||
uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group)
|
||||
@@ -6040,9 +6352,12 @@ enum BazaarTraderBarterActions {
|
||||
};
|
||||
|
||||
enum BazaarPurchaseActions {
|
||||
ByVendor = 0,
|
||||
ByParcel = 1,
|
||||
ByDirectToInventory = 2
|
||||
BazaarByVendor = 0,
|
||||
BazaarByParcel = 1,
|
||||
BazaarByDirectToInventory = 2,
|
||||
BarterByVendor = 0,
|
||||
BarterInBazaar = 1,
|
||||
BarterOutsideBazaar = 2
|
||||
};
|
||||
|
||||
enum BazaarPurchaseSubActions {
|
||||
@@ -6116,6 +6431,11 @@ struct BazaarSearchMessaging_Struct {
|
||||
}
|
||||
};
|
||||
|
||||
struct BuylineItemDetails_Struct {
|
||||
uint64 item_cost;
|
||||
uint32 item_quantity;
|
||||
};
|
||||
|
||||
// Restore structure packing to default
|
||||
#pragma pack()
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ void EQEmuConfig::parse_config()
|
||||
PluginDir = _root["server"]["directories"].get("plugins", "plugins/").asString();
|
||||
LuaModuleDir = _root["server"]["directories"].get("lua_modules", "lua_modules/").asString();
|
||||
PatchDir = _root["server"]["directories"].get("patches", "./").asString();
|
||||
OpcodeDir = _root["server"]["directories"].get("opcodes", "./").asString();
|
||||
SharedMemDir = _root["server"]["directories"].get("shared_memory", "shared/").asString();
|
||||
LogDir = _root["server"]["directories"].get("logs", "logs/").asString();
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ class EQEmuConfig
|
||||
std::string PluginDir;
|
||||
std::string LuaModuleDir;
|
||||
std::string PatchDir;
|
||||
std::string OpcodeDir;
|
||||
std::string SharedMemDir;
|
||||
std::string LogDir;
|
||||
|
||||
|
||||
@@ -789,50 +789,36 @@ std::string PlayerEventDiscordFormatter::FormatNPCHandinEvent(
|
||||
);
|
||||
}
|
||||
|
||||
std::string npc_info = fmt::format(
|
||||
"{} ({})\n",
|
||||
e.npc_name,
|
||||
e.npc_id
|
||||
);
|
||||
|
||||
npc_info += fmt::format(
|
||||
"Is Quest Handin: {}",
|
||||
e.is_quest_handin ? "Yes" : "No"
|
||||
);
|
||||
|
||||
std::vector<DiscordField> f = {};
|
||||
|
||||
|
||||
BuildDiscordField(&f, "NPC", npc_info);
|
||||
|
||||
if (!handin_items_info.empty()) {
|
||||
BuildDiscordField(
|
||||
&f,
|
||||
"Handin Items",
|
||||
fmt::format(
|
||||
"{}",
|
||||
handin_items_info
|
||||
)
|
||||
);
|
||||
BuildDiscordField(&f, "Handin Items", handin_items_info);
|
||||
}
|
||||
|
||||
if (!handin_money_info.empty()) {
|
||||
BuildDiscordField(
|
||||
&f,
|
||||
"Handin Money",
|
||||
fmt::format(
|
||||
"{}",
|
||||
handin_money_info
|
||||
)
|
||||
);
|
||||
BuildDiscordField(&f, "Handin Money", handin_money_info);
|
||||
}
|
||||
|
||||
if (!return_items_info.empty()) {
|
||||
BuildDiscordField(
|
||||
&f,
|
||||
"Return Items",
|
||||
fmt::format(
|
||||
"{}",
|
||||
return_items_info
|
||||
)
|
||||
);
|
||||
BuildDiscordField(&f, "Return Items", return_items_info);
|
||||
}
|
||||
|
||||
if (!return_money_info.empty()) {
|
||||
BuildDiscordField(
|
||||
&f,
|
||||
"Return Money",
|
||||
fmt::format(
|
||||
"{}",
|
||||
return_money_info
|
||||
)
|
||||
);
|
||||
BuildDiscordField(&f, "Return Money", return_money_info);
|
||||
}
|
||||
|
||||
std::vector<DiscordEmbed> embeds = {};
|
||||
|
||||
@@ -654,58 +654,59 @@ const int32_t RETENTION_DAYS_DEFAULT = 7;
|
||||
|
||||
void PlayerEventLogs::SetSettingsDefaults()
|
||||
{
|
||||
m_settings[PlayerEvent::GM_COMMAND].event_enabled = 1;
|
||||
m_settings[PlayerEvent::ZONING].event_enabled = 1;
|
||||
m_settings[PlayerEvent::AA_GAIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::AA_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::FORAGE_SUCCESS].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FORAGE_FAILURE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FISH_SUCCESS].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FISH_FAILURE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::ITEM_DESTROY].event_enabled = 1;
|
||||
m_settings[PlayerEvent::WENT_ONLINE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::WENT_OFFLINE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::LEVEL_GAIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::LEVEL_LOSS].event_enabled = 1;
|
||||
m_settings[PlayerEvent::LOOT_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::MERCHANT_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::MERCHANT_SELL].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GROUP_JOIN].event_enabled = 0;
|
||||
m_settings[PlayerEvent::GROUP_LEAVE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::RAID_JOIN].event_enabled = 0;
|
||||
m_settings[PlayerEvent::RAID_LEAVE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::GROUNDSPAWN_PICKUP].event_enabled = 1;
|
||||
m_settings[PlayerEvent::NPC_HANDIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SKILL_UP].event_enabled = 0;
|
||||
m_settings[PlayerEvent::TASK_ACCEPT].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TASK_UPDATE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TASK_COMPLETE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GIVE_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SAY].event_enabled = 0;
|
||||
m_settings[PlayerEvent::REZ_ACCEPTED].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DEATH].event_enabled = 1;
|
||||
m_settings[PlayerEvent::COMBINE_FAILURE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::COMBINE_SUCCESS].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DROPPED_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SPLIT_MONEY].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DZ_JOIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DZ_LEAVE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADER_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADER_SELL].event_enabled = 1;
|
||||
m_settings[PlayerEvent::BANDOLIER_CREATE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::BANDOLIER_SWAP].event_enabled = 0;
|
||||
m_settings[PlayerEvent::DISCOVER_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::POSSIBLE_HACK].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 0;
|
||||
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
|
||||
m_settings[PlayerEvent::ITEM_CREATION].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GM_COMMAND].event_enabled = 1;
|
||||
m_settings[PlayerEvent::ZONING].event_enabled = 1;
|
||||
m_settings[PlayerEvent::AA_GAIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::AA_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::FORAGE_SUCCESS].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FORAGE_FAILURE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FISH_SUCCESS].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FISH_FAILURE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::ITEM_DESTROY].event_enabled = 1;
|
||||
m_settings[PlayerEvent::WENT_ONLINE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::WENT_OFFLINE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::LEVEL_GAIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::LEVEL_LOSS].event_enabled = 1;
|
||||
m_settings[PlayerEvent::LOOT_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::MERCHANT_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::MERCHANT_SELL].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GROUP_JOIN].event_enabled = 0;
|
||||
m_settings[PlayerEvent::GROUP_LEAVE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::RAID_JOIN].event_enabled = 0;
|
||||
m_settings[PlayerEvent::RAID_LEAVE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::GROUNDSPAWN_PICKUP].event_enabled = 1;
|
||||
m_settings[PlayerEvent::NPC_HANDIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SKILL_UP].event_enabled = 0;
|
||||
m_settings[PlayerEvent::TASK_ACCEPT].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TASK_UPDATE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TASK_COMPLETE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GIVE_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SAY].event_enabled = 0;
|
||||
m_settings[PlayerEvent::REZ_ACCEPTED].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DEATH].event_enabled = 1;
|
||||
m_settings[PlayerEvent::COMBINE_FAILURE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::COMBINE_SUCCESS].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DROPPED_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SPLIT_MONEY].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DZ_JOIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DZ_LEAVE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADER_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADER_SELL].event_enabled = 1;
|
||||
m_settings[PlayerEvent::BANDOLIER_CREATE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::BANDOLIER_SWAP].event_enabled = 0;
|
||||
m_settings[PlayerEvent::DISCOVER_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::POSSIBLE_HACK].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 0;
|
||||
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
|
||||
m_settings[PlayerEvent::ITEM_CREATION].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GUILD_TRIBUTE_DONATE_PLAT].event_enabled = 1;
|
||||
m_settings[PlayerEvent::PARCEL_SEND].event_enabled = 1;
|
||||
m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1;
|
||||
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
||||
|
||||
@@ -60,7 +60,8 @@ namespace PlayerEvent {
|
||||
GUILD_TRIBUTE_DONATE_PLAT,
|
||||
PARCEL_SEND,
|
||||
PARCEL_RETRIEVE,
|
||||
PARCEL_DELETE,
|
||||
PARCEL_DELETE,
|
||||
BARTER_TRANSACTION,
|
||||
MAX // dont remove
|
||||
};
|
||||
|
||||
@@ -122,7 +123,8 @@ namespace PlayerEvent {
|
||||
"Guild Tribute Donate Platinum",
|
||||
"Parcel Item Sent",
|
||||
"Parcel Item Retrieved",
|
||||
"Parcel Prune Routine"
|
||||
"Parcel Prune Routine",
|
||||
"Barter Transaction"
|
||||
};
|
||||
|
||||
// Generic struct used by all events
|
||||
@@ -858,10 +860,12 @@ namespace PlayerEvent {
|
||||
|
||||
class HandinEntry {
|
||||
public:
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
uint16 charges;
|
||||
bool attuned;
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
std::vector<uint32> augment_ids;
|
||||
std::vector<std::string> augment_names;
|
||||
uint16 charges;
|
||||
bool attuned;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
@@ -870,6 +874,8 @@ namespace PlayerEvent {
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(augment_ids),
|
||||
CEREAL_NVP(augment_names),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(attuned)
|
||||
);
|
||||
@@ -903,6 +909,7 @@ namespace PlayerEvent {
|
||||
HandinMoney handin_money;
|
||||
std::vector<HandinEntry> return_items;
|
||||
HandinMoney return_money;
|
||||
bool is_quest_handin;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
@@ -914,7 +921,8 @@ namespace PlayerEvent {
|
||||
CEREAL_NVP(handin_items),
|
||||
CEREAL_NVP(handin_money),
|
||||
CEREAL_NVP(return_items),
|
||||
CEREAL_NVP(return_money)
|
||||
CEREAL_NVP(return_money),
|
||||
CEREAL_NVP(is_quest_handin)
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1081,6 +1089,32 @@ namespace PlayerEvent {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BarterTransaction {
|
||||
std::string status;
|
||||
uint32 item_id;
|
||||
uint32 item_quantity;
|
||||
std::string item_name;
|
||||
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||
std::string buyer_name;
|
||||
std::string seller_name;
|
||||
uint64 total_cost;
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(status),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_quantity),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(trade_items),
|
||||
CEREAL_NVP(buyer_name),
|
||||
CEREAL_NVP(seller_name),
|
||||
CEREAL_NVP(total_cost)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //EQEMU_PLAYER_EVENTS_H
|
||||
|
||||
+559
-467
File diff suppressed because it is too large
Load Diff
@@ -176,6 +176,7 @@ namespace EQ
|
||||
// Locate an available inventory slot
|
||||
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
|
||||
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
|
||||
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
|
||||
|
||||
// Calculate slot_id for an item within a bag
|
||||
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id
|
||||
@@ -197,12 +198,6 @@ namespace EQ
|
||||
|
||||
uint8 FindBrightestLightType();
|
||||
|
||||
void dumpEntireInventory();
|
||||
void dumpWornItems();
|
||||
void dumpInventory();
|
||||
void dumpBankItems();
|
||||
void dumpSharedBankItems();
|
||||
|
||||
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, const std::string& value);
|
||||
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, int value);
|
||||
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, float value);
|
||||
@@ -215,8 +210,6 @@ namespace EQ
|
||||
///////////////////////////////
|
||||
|
||||
int GetSlotByItemInstCollection(const std::map<int16, ItemInstance*> &collection, ItemInstance *inst);
|
||||
void dumpItemCollection(const std::map<int16, ItemInstance*> &collection);
|
||||
void dumpBagContents(ItemInstance *inst, std::map<int16, ItemInstance*>::const_iterator *it);
|
||||
|
||||
// Retrieves item within an inventory bucket
|
||||
ItemInstance* _GetItem(const std::map<int16, ItemInstance*>& bucket, int16 slot_id) const;
|
||||
|
||||
+55
-21
@@ -81,45 +81,79 @@ bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets local address - pings google to inspect what interface was used locally
|
||||
* @return
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
std::string IpUtil::GetLocalIPAddress()
|
||||
{
|
||||
char my_ip_address[16];
|
||||
unsigned int my_port;
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
char my_ip_address[INET_ADDRSTRLEN];
|
||||
struct sockaddr_in server_address{};
|
||||
struct sockaddr_in my_address{};
|
||||
int sockfd;
|
||||
int sockfd;
|
||||
|
||||
// Connect to server
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
// Create a UDP socket
|
||||
#ifdef _WIN32
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
return "";
|
||||
}
|
||||
#else
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0) {
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set server_addr
|
||||
// Set server_addr (dummy address)
|
||||
memset(&server_address, 0, sizeof(server_address));
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_addr.s_addr = inet_addr("172.217.160.99");
|
||||
server_address.sin_port = htons(80);
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS
|
||||
server_address.sin_port = htons(53); // DNS port
|
||||
|
||||
// Connect to server
|
||||
if (connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
|
||||
close(sockfd);
|
||||
return "";
|
||||
}
|
||||
// Perform a dummy connection to the server (UDP)
|
||||
connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address));
|
||||
|
||||
// Get my ip address and port
|
||||
// Get my IP address
|
||||
memset(&my_address, 0, sizeof(my_address));
|
||||
socklen_t len = sizeof(my_address);
|
||||
getsockname(sockfd, (struct sockaddr *) &my_address, &len);
|
||||
inet_ntop(AF_INET, &my_address.sin_addr, my_ip_address, sizeof(my_ip_address));
|
||||
my_port = ntohs(my_address.sin_port);
|
||||
|
||||
return fmt::format("{}", my_ip_address);
|
||||
#ifdef _WIN32
|
||||
closesocket(sockfd);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sockfd);
|
||||
#endif
|
||||
|
||||
LogInfo("Local IP Address [{}]", my_ip_address);
|
||||
|
||||
return std::string(my_ip_address);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets public address
|
||||
* Uses various websites as options to return raw public IP back to the client
|
||||
|
||||
+39
-36
@@ -32,10 +32,11 @@
|
||||
|
||||
//#include <iostream>
|
||||
|
||||
int32 NextItemInstSerialNumber = 1;
|
||||
|
||||
static inline int32 GetNextItemInstSerialNumber() {
|
||||
int32 next_item_serial_number = 1;
|
||||
std::unordered_set<uint64> guids{};
|
||||
|
||||
static inline int32 GetNextItemInstSerialNumber()
|
||||
{
|
||||
// The Bazaar relies on each item a client has up for Trade having a unique
|
||||
// identifier. This 'SerialNumber' is sent in Serialized item packets and
|
||||
// is used in Bazaar packets to identify the item a player is buying or inspecting.
|
||||
@@ -46,12 +47,18 @@ static inline int32 GetNextItemInstSerialNumber() {
|
||||
// NextItemInstSerialNumber is the next one to hand out.
|
||||
//
|
||||
// It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1.
|
||||
if(NextItemInstSerialNumber >= INT_MAX)
|
||||
NextItemInstSerialNumber = 1;
|
||||
else
|
||||
NextItemInstSerialNumber++;
|
||||
if (next_item_serial_number >= INT32_MAX) {
|
||||
next_item_serial_number = 1;
|
||||
}
|
||||
else {
|
||||
next_item_serial_number++;
|
||||
}
|
||||
|
||||
return NextItemInstSerialNumber;
|
||||
while (guids.contains(next_item_serial_number)) {
|
||||
next_item_serial_number++;
|
||||
}
|
||||
|
||||
return next_item_serial_number;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -303,47 +310,34 @@ int8 EQ::ItemInstance::AvailableAugmentSlot(int32 augment_type) const
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
auto i = invaug::SOCKET_BEGIN;
|
||||
for (; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetItem(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[i] &&
|
||||
((1 << (m_item->AugSlotType[i] - 1)) & augment_type)
|
||||
)
|
||||
) {
|
||||
break;
|
||||
for (int16 slot_id = invaug::SOCKET_BEGIN; slot_id <= invaug::SOCKET_END; ++slot_id) {
|
||||
if (IsAugmentSlotAvailable(augment_type, slot_id)) {
|
||||
return slot_id;
|
||||
}
|
||||
}
|
||||
|
||||
return (i <= invaug::SOCKET_END) ? i : INVALID_INDEX;
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
bool EQ::ItemInstance::IsAugmentSlotAvailable(int32 augment_type, uint8 slot) const
|
||||
{
|
||||
if (!m_item || !m_item->IsClassCommon()) {
|
||||
if (!m_item || !m_item->IsClassCommon() || GetItem(slot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
return (
|
||||
(
|
||||
!GetItem(slot) &&
|
||||
m_item->AugSlotVisible[slot]
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[slot] &&
|
||||
((1 << (m_item->AugSlotType[slot] - 1)) & augment_type)
|
||||
)
|
||||
) &&
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[slot] &&
|
||||
((1 << (m_item->AugSlotType[slot] - 1)) & augment_type)
|
||||
RuleB(Items, AugmentItemAllowInvisibleAugments) ||
|
||||
m_item->AugSlotVisible[slot]
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve item inside container
|
||||
@@ -1292,7 +1286,7 @@ int EQ::ItemInstance::GetItemBaneDamageRace(bool augments) const
|
||||
return race;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemBaneDamageBody(bodyType against, bool augments) const
|
||||
int EQ::ItemInstance::GetItemBaneDamageBody(uint8 against, bool augments) const
|
||||
{
|
||||
int64 damage = 0;
|
||||
const auto item = GetItem();
|
||||
@@ -1948,6 +1942,15 @@ int EQ::ItemInstance::GetItemSkillsStat(EQ::skills::SkillType skill, bool augmen
|
||||
return stat;
|
||||
}
|
||||
|
||||
void EQ::ItemInstance::AddGUIDToMap(uint64 existing_serial_number)
|
||||
{
|
||||
guids.emplace(existing_serial_number);
|
||||
}
|
||||
|
||||
void EQ::ItemInstance::ClearGUIDMap()
|
||||
{
|
||||
guids.clear();
|
||||
}
|
||||
//
|
||||
// class EvolveInfo
|
||||
//
|
||||
|
||||
@@ -265,7 +265,7 @@ namespace EQ
|
||||
// these two are just quick checks
|
||||
int GetItemBaneDamageBody(bool augments = false) const;
|
||||
int GetItemBaneDamageRace(bool augments = false) const;
|
||||
int GetItemBaneDamageBody(bodyType against, bool augments = false) const;
|
||||
int GetItemBaneDamageBody(uint8 against, bool augments = false) const;
|
||||
int GetItemBaneDamageRace(uint16 against, bool augments = false) const;
|
||||
int GetItemMagical(bool augments = false) const;
|
||||
int GetItemHP(bool augments = false) const;
|
||||
@@ -309,6 +309,8 @@ namespace EQ
|
||||
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
|
||||
uint32 GetItemGuildFavor() const;
|
||||
std::vector<uint32> GetAugmentIDs() const;
|
||||
static void AddGUIDToMap(uint64 existing_serial_number);
|
||||
static void ClearGUIDMap();
|
||||
|
||||
protected:
|
||||
//////////////////////////
|
||||
|
||||
@@ -0,0 +1,586 @@
|
||||
#include "mysql_stmt.h"
|
||||
#include "eqemu_logsys.h"
|
||||
#include "mutex.h"
|
||||
#include "timer.h"
|
||||
#include <charconv>
|
||||
|
||||
namespace mysql
|
||||
{
|
||||
|
||||
void PreparedStmt::StmtDeleter::operator()(MYSQL_STMT* stmt) noexcept
|
||||
{
|
||||
// The connection must be locked when closing the stmt to avoid mysql errors
|
||||
// in case another thread tries to use it during the close. If the mutex is
|
||||
// changed to one that throws then exceptions need to be caught here.
|
||||
LockMutex lock(mutex);
|
||||
mysql_stmt_close(stmt);
|
||||
}
|
||||
|
||||
PreparedStmt::PreparedStmt(MYSQL& mysql, std::string query, Mutex* mutex, StmtOptions opts)
|
||||
: m_stmt(mysql_stmt_init(&mysql), { mutex }), m_query(std::move(query)), m_mutex(mutex), m_options(opts)
|
||||
{
|
||||
LockMutex lock(m_mutex);
|
||||
if (mysql_stmt_prepare(m_stmt.get(), m_query.c_str(), static_cast<unsigned long>(m_query.size())) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Prepare error: {}", GetStmtError()));
|
||||
}
|
||||
|
||||
m_params.resize(mysql_stmt_param_count(m_stmt.get()));
|
||||
m_inputs.resize(m_params.size());
|
||||
}
|
||||
|
||||
void PreparedStmt::ThrowError(const std::string& error)
|
||||
{
|
||||
LogMySQLError("{}", error);
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
std::string PreparedStmt::GetStmtError()
|
||||
{
|
||||
auto err = mysql_stmt_errno(m_stmt.get());
|
||||
auto str = mysql_stmt_error(m_stmt.get());
|
||||
return fmt::format("({}) [{}] for query [{}]", err, str, m_query);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PreparedStmt::BindInput(size_t index, T value)
|
||||
{
|
||||
if (index >= m_inputs.size())
|
||||
{
|
||||
ThrowError(fmt::format("Cannot bind input, index {} out of range", index));
|
||||
}
|
||||
|
||||
impl::Bind& arg = m_inputs[index];
|
||||
arg.is_null = std::is_same_v<T, std::nullptr_t>;
|
||||
|
||||
MYSQL_BIND& bind = m_params[index];
|
||||
bind.is_unsigned = std::is_unsigned_v<T>;
|
||||
bind.is_null = &arg.is_null;
|
||||
bind.length = &arg.length;
|
||||
|
||||
auto old_type = bind.buffer_type;
|
||||
|
||||
if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
if (arg.buffer.size() < sizeof(T))
|
||||
{
|
||||
arg.buffer.resize(std::max(sizeof(T), sizeof(int64_t)));
|
||||
bind.buffer = arg.buffer.data();
|
||||
m_need_bind = true;
|
||||
}
|
||||
memcpy(arg.buffer.data(), &value, sizeof(T));
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, uint8_t> || std::is_same_v<T, bool>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_TINY;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, int16_t> || std::is_same_v<T, uint16_t>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_SHORT;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_LONG;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_LONGLONG;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_FLOAT;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_DOUBLE;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string_view>)
|
||||
{
|
||||
bind.buffer_type = MYSQL_TYPE_STRING;
|
||||
if (arg.buffer.empty() || arg.buffer.size() < value.size())
|
||||
{
|
||||
arg.buffer.resize(static_cast<size_t>((value.size() + 1) * 1.5));
|
||||
bind.buffer = arg.buffer.data();
|
||||
bind.buffer_length = static_cast<unsigned long>(arg.buffer.size());
|
||||
m_need_bind = true;
|
||||
}
|
||||
std::copy(value.begin(), value.end(), arg.buffer.begin());
|
||||
arg.length = static_cast<unsigned long>(value.size());
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, std::nullptr_t>)
|
||||
{
|
||||
static_assert(false_v<T>, "Cannot bind unsupported type");
|
||||
}
|
||||
|
||||
if (old_type != bind.buffer_type)
|
||||
{
|
||||
m_need_bind = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedStmt::BindInput(size_t index, const char* str)
|
||||
{
|
||||
BindInput(index, std::string_view(str));
|
||||
}
|
||||
|
||||
void PreparedStmt::BindInput(size_t index, const std::string& str)
|
||||
{
|
||||
BindInput(index, std::string_view(str));
|
||||
}
|
||||
|
||||
StmtResult PreparedStmt::Execute()
|
||||
{
|
||||
CheckArgs(0);
|
||||
return DoExecute();
|
||||
}
|
||||
|
||||
StmtResult PreparedStmt::Execute(const std::vector<param_t>& args)
|
||||
{
|
||||
CheckArgs(args.size());
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
std::visit([&](const auto& arg) { BindInput(i, arg); }, args[i]);
|
||||
}
|
||||
return DoExecute();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
StmtResult PreparedStmt::Execute(const std::vector<T>& args)
|
||||
{
|
||||
CheckArgs(args.size());
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
BindInput(i, args[i]);
|
||||
}
|
||||
return DoExecute();
|
||||
}
|
||||
|
||||
void PreparedStmt::CheckArgs(size_t argc)
|
||||
{
|
||||
if (argc != m_params.size())
|
||||
{
|
||||
ThrowError(fmt::format("Bad arg count (got {}, expected {}) for [{}]", argc, m_params.size(), m_query));
|
||||
}
|
||||
}
|
||||
|
||||
StmtResult PreparedStmt::DoExecute()
|
||||
{
|
||||
BenchTimer timer;
|
||||
LockMutex lock(m_mutex);
|
||||
|
||||
if (m_need_bind && mysql_stmt_bind_param(m_stmt.get(), m_params.data()) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Bind param error: {}", GetStmtError()));
|
||||
}
|
||||
|
||||
m_need_bind = false;
|
||||
|
||||
if (mysql_stmt_execute(m_stmt.get()) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Execute error: {}", GetStmtError()));
|
||||
}
|
||||
|
||||
my_bool attr = m_options.use_max_length;
|
||||
mysql_stmt_attr_set(m_stmt.get(), STMT_ATTR_UPDATE_MAX_LENGTH, &attr);
|
||||
|
||||
if (m_options.buffer_results && mysql_stmt_store_result(m_stmt.get()) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Store result error: {}", GetStmtError()));
|
||||
}
|
||||
|
||||
// Result buffers are bound on first execute and re-used if needed
|
||||
if (m_results.empty())
|
||||
{
|
||||
BindResults();
|
||||
}
|
||||
|
||||
StmtResult res(m_stmt.get(), m_results.size());
|
||||
|
||||
if (m_results.empty())
|
||||
{
|
||||
LogMySQLQuery("{} -- ({} row(s) affected) ({:.6f}s)", m_query, res.RowsAffected(), timer.elapsed());
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMySQLQuery("{} -- ({} row(s) returned) ({:.6f}s)", m_query, res.RowCount(), timer.elapsed());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void PreparedStmt::BindResults()
|
||||
{
|
||||
MYSQL_RES* res = mysql_stmt_result_metadata(m_stmt.get());
|
||||
if (!res)
|
||||
{
|
||||
return; // did not produce a result set
|
||||
}
|
||||
|
||||
MYSQL_FIELD* fields = mysql_fetch_fields(res);
|
||||
m_columns.resize(mysql_num_fields(res));
|
||||
m_results.resize(m_columns.size());
|
||||
|
||||
for (int i = 0; i < static_cast<int>(m_columns.size()); ++i)
|
||||
{
|
||||
impl::BindColumn& col = m_columns[i].m_col;
|
||||
MYSQL_BIND& bind = m_results[i];
|
||||
|
||||
col.index = i;
|
||||
col.name = fields[i].name;
|
||||
col.buffer_type = fields[i].type;
|
||||
col.is_unsigned = (fields[i].flags & UNSIGNED_FLAG) != 0;
|
||||
col.buffer.resize(GetResultBufferSize(fields[i]));
|
||||
|
||||
bind.buffer_type = col.buffer_type;
|
||||
bind.buffer = col.buffer.data();
|
||||
bind.buffer_length = static_cast<unsigned long>(col.buffer.size());
|
||||
bind.is_unsigned = col.is_unsigned;
|
||||
bind.is_null = &col.is_null;
|
||||
bind.length = &col.length;
|
||||
bind.error = &col.error;
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
|
||||
if (!m_results.empty() && mysql_stmt_bind_result(m_stmt.get(), m_results.data()) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Bind result error: {}", GetStmtError()));
|
||||
}
|
||||
}
|
||||
|
||||
int PreparedStmt::GetResultBufferSize(const MYSQL_FIELD& field) const
|
||||
{
|
||||
switch (field.type)
|
||||
{
|
||||
case MYSQL_TYPE_TINY:
|
||||
return sizeof(int8_t);
|
||||
case MYSQL_TYPE_SHORT:
|
||||
return sizeof(int16_t);
|
||||
case MYSQL_TYPE_INT24:
|
||||
case MYSQL_TYPE_LONG:
|
||||
return sizeof(int32_t);
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
return sizeof(int64_t);
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
return sizeof(float);
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
return sizeof(double);
|
||||
case MYSQL_TYPE_TIME:
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
return sizeof(MYSQL_TIME);
|
||||
default: // if max_length is unavailable for strings buffers are resized on fetch
|
||||
return field.max_length + 1; // ensure valid buffer created
|
||||
}
|
||||
}
|
||||
|
||||
StmtRow PreparedStmt::Fetch()
|
||||
{
|
||||
StmtRow row;
|
||||
if (!m_columns.empty())
|
||||
{
|
||||
int rc = mysql_stmt_fetch(m_stmt.get());
|
||||
if (rc == 1)
|
||||
{
|
||||
ThrowError(fmt::format("Fetch error: {}", GetStmtError()));
|
||||
}
|
||||
|
||||
if (rc != MYSQL_NO_DATA)
|
||||
{
|
||||
if (rc == MYSQL_DATA_TRUNCATED)
|
||||
{
|
||||
FetchTruncated();
|
||||
}
|
||||
row = StmtRow(m_columns);
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
void PreparedStmt::FetchTruncated()
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(m_columns.size()); ++i)
|
||||
{
|
||||
impl::BindColumn& col = m_columns[i].m_col;
|
||||
if (col.error)
|
||||
{
|
||||
MYSQL_BIND& bind = m_results[i];
|
||||
col.buffer.resize(static_cast<size_t>(col.length * 1.5));
|
||||
bind.buffer = col.buffer.data();
|
||||
bind.buffer_length = static_cast<unsigned long>(col.buffer.size());
|
||||
|
||||
mysql_stmt_fetch_column(m_stmt.get(), &bind, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mysql_stmt_bind_result(m_stmt.get(), m_results.data()) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Fetch rebind result error: {}", GetStmtError()));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
StmtResult::StmtResult(MYSQL_STMT* stmt, size_t columns)
|
||||
{
|
||||
m_num_cols = static_cast<int>(columns);
|
||||
m_num_rows = mysql_stmt_num_rows(stmt); // requires buffered results
|
||||
m_affected = mysql_stmt_affected_rows(stmt);
|
||||
m_insert_id = mysql_stmt_insert_id(stmt);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const StmtColumn* StmtRow::GetColumn(size_t index) const
|
||||
{
|
||||
return index < m_columns.size() ? &m_columns[index] : nullptr;
|
||||
}
|
||||
|
||||
const StmtColumn* StmtRow::GetColumn(std::string_view name) const
|
||||
{
|
||||
auto it = std::ranges::find_if(m_columns,
|
||||
[name](const StmtColumn& col) { return col.Name() == name; });
|
||||
|
||||
return it != m_columns.end() ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::string> StmtRow::operator[](size_t index) const
|
||||
{
|
||||
return GetStr(index);
|
||||
}
|
||||
|
||||
std::optional<std::string> StmtRow::operator[](std::string_view name) const
|
||||
{
|
||||
return GetStr(name);
|
||||
}
|
||||
|
||||
std::optional<std::string> StmtRow::GetStr(size_t index) const
|
||||
{
|
||||
const StmtColumn* col = GetColumn(index);
|
||||
return col ? col->GetStr() : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> StmtRow::GetStr(std::string_view name) const
|
||||
{
|
||||
const StmtColumn* col = GetColumn(name);
|
||||
return col ? col->GetStr() : std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T> requires std::is_arithmetic_v<T>
|
||||
std::optional<T> StmtRow::Get(size_t index) const
|
||||
{
|
||||
const StmtColumn* col = GetColumn(index);
|
||||
return col ? col->Get<T>() : std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T> requires std::is_arithmetic_v<T>
|
||||
std::optional<T> StmtRow::Get(std::string_view name) const
|
||||
{
|
||||
const StmtColumn* col = GetColumn(name);
|
||||
return col ? col->Get<T>() : std::nullopt;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static time_t MakeTime(const MYSQL_TIME& mt)
|
||||
{
|
||||
// buffer mt given in mysql session time zone (assumes local)
|
||||
std::tm tm{};
|
||||
tm.tm_year = mt.year - 1900;
|
||||
tm.tm_mon = mt.month - 1;
|
||||
tm.tm_mday = mt.day;
|
||||
tm.tm_hour = mt.hour;
|
||||
tm.tm_min = mt.minute;
|
||||
tm.tm_sec = mt.second;
|
||||
tm.tm_isdst = -1;
|
||||
return std::mktime(&tm);
|
||||
}
|
||||
|
||||
static int MakeSeconds(const MYSQL_TIME& mt)
|
||||
{
|
||||
return (mt.neg ? -1 : 1) * static_cast<int>(mt.hour * 3600 + mt.minute * 60 + mt.second);
|
||||
}
|
||||
|
||||
static uint64_t MakeBits(std::span<const uint8_t> data)
|
||||
{
|
||||
// byte stream for bits is in big endian
|
||||
uint64_t bits = 0;
|
||||
for (size_t i = 0; i < data.size() && i < sizeof(uint64_t); ++i)
|
||||
{
|
||||
bits |= static_cast<uint64_t>(data[data.size() - i - 1] & 0xff) << (i * 8);
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T FromString(std::string_view sv)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
{
|
||||
// return false for empty (zero-length) strings
|
||||
return !sv.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
// non numbers return a zero initialized T (could return nullopt instead)
|
||||
T value = {};
|
||||
std::from_chars(sv.data(), sv.data() + sv.size(), value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string FormatTime(enum_field_types type, const MYSQL_TIME& mt)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MYSQL_TYPE_TIME: // hhh:mm:ss '-838:59:59' to '838:59:59'
|
||||
return fmt::format("{}{:02d}:{:02d}:{:02d}", mt.neg ? "-" : "", mt.hour, mt.minute, mt.second);
|
||||
case MYSQL_TYPE_DATE: // YYYY-MM-DD '1000-01-01' to '9999-12-31'
|
||||
return fmt::format("{}-{:02d}-{:02d}", mt.year, mt.month, mt.day);
|
||||
case MYSQL_TYPE_DATETIME: // YYYY-MM-DD hh:mm:ss '1000-01-01 00:00:00' to '9999-12-31 23:59:59'
|
||||
case MYSQL_TYPE_TIMESTAMP: // YYYY-MM-DD hh:mm:ss '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC
|
||||
return fmt::format("{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}", mt.year, mt.month, mt.day, mt.hour, mt.minute, mt.second);
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string_view> StmtColumn::GetStrView() const
|
||||
{
|
||||
if (m_col.is_null)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch (m_col.buffer_type)
|
||||
{
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
case MYSQL_TYPE_STRING:
|
||||
return std::make_optional<std::string_view>(reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length);
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> StmtColumn::GetStr() const
|
||||
{
|
||||
if (m_col.is_null)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch (m_col.buffer_type)
|
||||
{
|
||||
case MYSQL_TYPE_TINY:
|
||||
return m_col.is_unsigned ? fmt::format_int(BitCast<uint8_t>()).c_str() : fmt::format_int(BitCast<int8_t>()).c_str();
|
||||
case MYSQL_TYPE_SHORT:
|
||||
return m_col.is_unsigned ? fmt::format_int(BitCast<uint16_t>()).c_str() : fmt::format_int(BitCast<int16_t>()).c_str();
|
||||
case MYSQL_TYPE_INT24:
|
||||
case MYSQL_TYPE_LONG:
|
||||
return m_col.is_unsigned ? fmt::format_int(BitCast<uint32_t>()).c_str() : fmt::format_int(BitCast<int32_t>()).c_str();
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
return m_col.is_unsigned ? fmt::format_int(BitCast<uint64_t>()).c_str() : fmt::format_int(BitCast<int64_t>()).c_str();
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
return fmt::format("{}", BitCast<float>());
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
return fmt::format("{}", BitCast<double>());
|
||||
case MYSQL_TYPE_TIME:
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
return FormatTime(m_col.buffer_type, BitCast<MYSQL_TIME>());
|
||||
case MYSQL_TYPE_BIT:
|
||||
return fmt::format_int(*Get<uint64_t>()).c_str();
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
case MYSQL_TYPE_STRING:
|
||||
return std::make_optional<std::string>(reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length);
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> requires std::is_arithmetic_v<T>
|
||||
std::optional<T> StmtColumn::Get() const
|
||||
{
|
||||
if (m_col.is_null)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch (m_col.buffer_type)
|
||||
{
|
||||
case MYSQL_TYPE_TINY:
|
||||
return m_col.is_unsigned ? static_cast<T>(BitCast<uint8_t>()) : static_cast<T>(BitCast<int8_t>());
|
||||
case MYSQL_TYPE_SHORT:
|
||||
return m_col.is_unsigned ? static_cast<T>(BitCast<uint16_t>()) : static_cast<T>(BitCast<int16_t>());
|
||||
case MYSQL_TYPE_INT24:
|
||||
case MYSQL_TYPE_LONG:
|
||||
return m_col.is_unsigned ? static_cast<T>(BitCast<uint32_t>()) : static_cast<T>(BitCast<int32_t>());
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
return m_col.is_unsigned ? static_cast<T>(BitCast<uint64_t>()) : static_cast<T>(BitCast<int64_t>());
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
return static_cast<T>(BitCast<float>());
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
return static_cast<T>(BitCast<double>());
|
||||
case MYSQL_TYPE_TIME: // return as total seconds
|
||||
return static_cast<T>(MakeSeconds(BitCast<MYSQL_TIME>()));
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_TIMESTAMP: // return as epoch timestamp
|
||||
return static_cast<T>(MakeTime(BitCast<MYSQL_TIME>()));
|
||||
case MYSQL_TYPE_BIT:
|
||||
return static_cast<T>(MakeBits({ m_col.buffer.data(), m_col.length }));
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
case MYSQL_TYPE_STRING:
|
||||
return FromString<T>({ reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length });
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// explicit template instantiations for supported types
|
||||
template void PreparedStmt::BindInput(size_t, std::string_view);
|
||||
template void PreparedStmt::BindInput(size_t, std::nullptr_t);
|
||||
template StmtResult PreparedStmt::Execute(const std::vector<std::string_view>&);
|
||||
template StmtResult PreparedStmt::Execute(const std::vector<std::string>&);
|
||||
template StmtResult PreparedStmt::Execute(const std::vector<const char*>&);
|
||||
|
||||
#define INSTANTIATE(T) \
|
||||
template void PreparedStmt::BindInput(size_t, T); \
|
||||
template StmtResult PreparedStmt::Execute(const std::vector<T>&); \
|
||||
template std::optional<T> StmtRow::Get(size_t) const; \
|
||||
template std::optional<T> StmtRow::Get(std::string_view) const; \
|
||||
template std::optional<T> StmtColumn::Get() const;
|
||||
|
||||
INSTANTIATE(bool);
|
||||
INSTANTIATE(int8_t);
|
||||
INSTANTIATE(uint8_t);
|
||||
INSTANTIATE(int16_t);
|
||||
INSTANTIATE(uint16_t);
|
||||
INSTANTIATE(int32_t);
|
||||
INSTANTIATE(uint32_t);
|
||||
INSTANTIATE(int64_t);
|
||||
INSTANTIATE(uint64_t);
|
||||
INSTANTIATE(float);
|
||||
INSTANTIATE(double);
|
||||
|
||||
} // namespace mysql
|
||||
@@ -0,0 +1,221 @@
|
||||
#pragma once
|
||||
|
||||
#include "mysql.h"
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
class Mutex;
|
||||
|
||||
namespace mysql
|
||||
{
|
||||
|
||||
// support MySQL 8.0.1+ API which removed the my_bool type
|
||||
#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80001
|
||||
using my_bool = bool;
|
||||
#endif
|
||||
|
||||
template <typename>
|
||||
inline constexpr bool false_v = false;
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
struct Bind
|
||||
{
|
||||
std::vector<uint8_t> buffer;
|
||||
unsigned long length = 0;
|
||||
my_bool is_null = false;
|
||||
my_bool error = false;
|
||||
};
|
||||
|
||||
struct BindColumn : Bind
|
||||
{
|
||||
int index = 0;
|
||||
std::string name;
|
||||
bool is_unsigned = false;
|
||||
enum_field_types buffer_type = {};
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct StmtOptions
|
||||
{
|
||||
// Enable buffering (storing) entire result set after executing a statement
|
||||
bool buffer_results = true;
|
||||
|
||||
// Enable MySQL to update max_length of fields in execute result set (requires buffering)
|
||||
bool use_max_length = true;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Holds ownership of bound column value buffer
|
||||
class StmtColumn
|
||||
{
|
||||
public:
|
||||
int Index() const { return m_col.index; }
|
||||
bool IsNull() const { return m_col.is_null; }
|
||||
bool IsUnsigned() const { return m_col.is_unsigned; }
|
||||
enum_field_types Type() const { return m_col.buffer_type; }
|
||||
const std::string& Name() const { return m_col.name; }
|
||||
|
||||
// Get view of column value buffer
|
||||
std::span<const uint8_t> GetBuf() const { return { m_col.buffer.data(), m_col.length }; }
|
||||
|
||||
// Get view of column string value. Returns nullopt if value is NULL or not a string
|
||||
std::optional<std::string_view> GetStrView() const;
|
||||
|
||||
// Get column value as string. Returns nullopt if value is NULL or field type unsupported
|
||||
std::optional<std::string> GetStr() const;
|
||||
|
||||
// Get column value as numeric T. Returns nullopt if value NULL or field type unsupported
|
||||
template <typename T> requires std::is_arithmetic_v<T>
|
||||
std::optional<T> Get() const;
|
||||
|
||||
private:
|
||||
// uses memcpy for type punning buffer data to avoid UB with strict aliasing
|
||||
template <typename T>
|
||||
T BitCast() const
|
||||
{
|
||||
T val;
|
||||
assert(sizeof(T) == m_col.length);
|
||||
memcpy(&val, m_col.buffer.data(), sizeof(T));
|
||||
return val;
|
||||
}
|
||||
|
||||
friend class PreparedStmt; // access to allocate and bind buffers
|
||||
friend class StmtResult; // access to resize truncated buffers
|
||||
impl::BindColumn m_col;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Provides a non-owning view of PreparedStmt column value buffers
|
||||
// Evaluates false if it does not contain a valid row
|
||||
class StmtRow
|
||||
{
|
||||
public:
|
||||
StmtRow() = default;
|
||||
StmtRow(std::span<const StmtColumn> columns) : m_columns(columns) {};
|
||||
|
||||
explicit operator bool() const noexcept { return !m_columns.empty(); }
|
||||
|
||||
int ColumnCount() const { return static_cast<int>(m_columns.size()); }
|
||||
const StmtColumn* GetColumn(size_t index) const;
|
||||
const StmtColumn* GetColumn(std::string_view name) const;
|
||||
|
||||
// Get specified column value as string
|
||||
// Returns nullopt if column invalid, value is NULL, or field type unsupported
|
||||
std::optional<std::string> operator[](size_t index) const;
|
||||
std::optional<std::string> operator[](std::string_view name) const;
|
||||
std::optional<std::string> GetStr(size_t index) const;
|
||||
std::optional<std::string> GetStr(std::string_view name) const;
|
||||
|
||||
// Get specified column value as numeric T
|
||||
// Returns nullopt if column invalid, value is NULL, or field type unsupported
|
||||
template <typename T> requires std::is_arithmetic_v<T>
|
||||
std::optional<T> Get(size_t index) const;
|
||||
|
||||
template <typename T> requires std::is_arithmetic_v<T>
|
||||
std::optional<T> Get(std::string_view name) const;
|
||||
|
||||
auto begin() const { return m_columns.begin(); }
|
||||
auto end() const { return m_columns.end(); }
|
||||
|
||||
private:
|
||||
std::span<const StmtColumn> m_columns;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Result meta data for an executed prepared statement
|
||||
class StmtResult
|
||||
{
|
||||
public:
|
||||
StmtResult() = default;
|
||||
StmtResult(MYSQL_STMT* stmt, size_t columns);
|
||||
|
||||
int ColumnCount() const { return m_num_cols; }
|
||||
uint64_t RowCount() const { return m_num_rows; }
|
||||
uint64_t RowsAffected() const { return m_affected; }
|
||||
uint64_t LastInsertID() const { return m_insert_id; }
|
||||
|
||||
private:
|
||||
int m_num_cols = 0;
|
||||
uint64_t m_num_rows = 0;
|
||||
uint64_t m_affected = 0;
|
||||
uint64_t m_insert_id = 0;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class PreparedStmt
|
||||
{
|
||||
public:
|
||||
// Supported argument types for execute
|
||||
using param_t = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
|
||||
int64_t, uint64_t, float, double, bool, std::string_view, std::nullptr_t>;
|
||||
|
||||
PreparedStmt() = delete;
|
||||
PreparedStmt(MYSQL& mysql, std::string query, Mutex* mutex, StmtOptions opts = {});
|
||||
|
||||
const std::string& GetQuery() const { return m_query; }
|
||||
StmtOptions GetOptions() const { return m_options; }
|
||||
void SetOptions(StmtOptions options) { m_options = options; }
|
||||
void FreeResult() { mysql_stmt_free_result(m_stmt.get()); }
|
||||
|
||||
// Execute the prepared statement with specified arguments
|
||||
// Throws exception on error
|
||||
template <typename T>
|
||||
StmtResult Execute(const std::vector<T>& args);
|
||||
StmtResult Execute(const std::vector<param_t>& args);
|
||||
StmtResult Execute();
|
||||
|
||||
// Fetch the next row into column buffers (overwrites previous row values)
|
||||
// Return value evaluates false if no more rows to fetch
|
||||
// Throws exception on error
|
||||
StmtRow Fetch();
|
||||
|
||||
private:
|
||||
void CheckArgs(size_t argc);
|
||||
StmtResult DoExecute();
|
||||
void BindResults();
|
||||
void FetchTruncated();
|
||||
int GetResultBufferSize(const MYSQL_FIELD& field) const;
|
||||
void ThrowError(const std::string& error);
|
||||
std::string GetStmtError();
|
||||
|
||||
// bind an input value to a query parameter by index
|
||||
template <typename T>
|
||||
void BindInput(size_t index, T value);
|
||||
void BindInput(size_t index, const char* str);
|
||||
void BindInput(size_t index, const std::string& str);
|
||||
|
||||
struct StmtDeleter
|
||||
{
|
||||
Mutex* mutex = nullptr;
|
||||
void operator()(MYSQL_STMT* stmt) noexcept;
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<MYSQL_STMT, StmtDeleter> m_stmt;
|
||||
std::vector<MYSQL_BIND> m_params; // input binds
|
||||
std::vector<MYSQL_BIND> m_results; // result binds
|
||||
std::vector<impl::Bind> m_inputs; // execute buffers (addresses bound)
|
||||
std::vector<StmtColumn> m_columns; // fetch buffers (addresses bound)
|
||||
std::string m_query;
|
||||
StmtOptions m_options = {};
|
||||
bool m_need_bind = true;
|
||||
Mutex* m_mutex = nullptr; // connection mutex
|
||||
};
|
||||
|
||||
} // namespace mysql
|
||||
+523
-32
@@ -356,41 +356,91 @@ namespace RoF2
|
||||
|
||||
ENCODE(OP_Barter)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
char *Buffer = (char *)in->pBuffer;
|
||||
char *buffer = (char *) in->pBuffer;
|
||||
uint32 sub_action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
|
||||
uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
|
||||
switch (sub_action) {
|
||||
case Barter_BuyerAppearance: {
|
||||
auto emu = (BuyerInspectRequest_Struct *) in->pBuffer;
|
||||
|
||||
if (SubAction != Barter_BuyerAppearance)
|
||||
{
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
auto outapp = new EQApplicationPacket(OP_Barter, sizeof(structs::Buyer_SetAppearance_Struct));
|
||||
auto eq = (structs::Buyer_SetAppearance_Struct *) outapp->pBuffer;
|
||||
|
||||
return;
|
||||
eq->action = structs::RoF2BuyerActions::BuyerAppearance;
|
||||
eq->entity_id = emu->buyer_id;
|
||||
eq->enabled = emu->approval;
|
||||
|
||||
dest->FastQueuePacket(&outapp);
|
||||
safe_delete(in);
|
||||
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerItemRemove: {
|
||||
auto emu = (BuyerRemoveItem_Struct *) in->pBuffer;
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_BuyerItems, sizeof(structs::BuyerRemoveItem_Struct));
|
||||
auto eq = (structs::BuyerRemoveItem_Struct *) outapp->pBuffer;
|
||||
|
||||
eq->action = structs::RoF2BuyerActions::BuyerModifyBuyLine;
|
||||
eq->slot_id = emu->buy_slot_id;
|
||||
eq->toggle = 0;
|
||||
|
||||
dest->FastQueuePacket(&outapp);
|
||||
safe_delete(in);
|
||||
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerInspectBegin: {
|
||||
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerInspectBegin;
|
||||
dest->FastQueuePacket(&in);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerInspectEnd: {
|
||||
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerInspectEnd;
|
||||
dest->FastQueuePacket(&in);
|
||||
break;
|
||||
}
|
||||
case Barter_SellerBrowsing: {
|
||||
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerBrowsingBuyLine;
|
||||
dest->FastQueuePacket(&in);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerSearchResults: {
|
||||
BuyerItemSearchResults_Struct bisr{};
|
||||
auto emu = (BuyerGeneric_Struct *) in->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(emu->payload),
|
||||
in->size - sizeof(BuyerGeneric_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(bisr);
|
||||
|
||||
LogTradingDetail("Sending item search results <green>[{}]", bisr.result_count);
|
||||
|
||||
uint32 packet_size = bisr.result_count * sizeof(structs::BuyerItemSearchResultEntry_Struct) + 8;
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_Barter, packet_size);
|
||||
auto eq = (char *) outapp->pBuffer;
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSearchResults);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bisr.result_count);
|
||||
for (auto const &i: bisr.results) {
|
||||
strn0cpy(eq, i.item_name, 64);
|
||||
eq += 64;
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
|
||||
VARSTRUCT_SKIP_TYPE(uint32, eq);
|
||||
}
|
||||
dest->QueuePacket(outapp.get());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTradingDetail("Unhandled action <red>[{}]", sub_action);
|
||||
dest->FastQueuePacket(&in);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
|
||||
in->size = 80;
|
||||
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
|
||||
char Name[64];
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction);
|
||||
uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID);
|
||||
uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
||||
VARSTRUCT_DECODE_STRING(Name, Buffer);
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, Name);
|
||||
OutBuffer = (char *)in->pBuffer + 72;
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle);
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
|
||||
ENCODE(OP_BazaarSearch)
|
||||
@@ -542,8 +592,8 @@ namespace RoF2
|
||||
LogTrading(
|
||||
"(RoF2) AddTraderToBazaarWindow action <green>[{}] trader_id <green>[{}] entity_id <green>[{}] zone_id <green>[{}]",
|
||||
eq->action,
|
||||
eq->entity_id,
|
||||
eq->trader_id,
|
||||
eq->entity_id,
|
||||
eq->zone_id
|
||||
);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
@@ -682,6 +732,243 @@ namespace RoF2
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_BuyerItems)
|
||||
{
|
||||
EQApplicationPacket *inapp = *p;
|
||||
*p = nullptr;
|
||||
|
||||
auto action = *(uint32 *) inapp->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case Barter_BuyerItemUpdate: {
|
||||
BuyerLineItems_Struct bl{};
|
||||
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(emu->payload),
|
||||
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(bl);
|
||||
|
||||
//packet size
|
||||
auto packet_size = bl.item_name.length() + 1 + 34;
|
||||
for (auto const &b: bl.trade_items) {
|
||||
packet_size += b.item_name.length() + 1;
|
||||
packet_size += 12;
|
||||
}
|
||||
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
|
||||
char *eq = (char *) outapp->pBuffer;
|
||||
auto no_trade_items = bl.trade_items.size();
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerModifyBuyLine);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.slot);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, bl.enabled ? 1 : 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_id);
|
||||
VARSTRUCT_ENCODE_STRING(eq, bl.item_name.c_str());
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_icon);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, bl.item_toggle ? 1 : 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_cost);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_trade_items);
|
||||
|
||||
for (int i = 0; i < no_trade_items; i++) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_icon);
|
||||
VARSTRUCT_ENCODE_STRING(eq, bl.trade_items[i].item_name.c_str());
|
||||
}
|
||||
dest->QueuePacket(outapp.get());
|
||||
safe_delete(inapp);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerInspectBegin: {
|
||||
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||
|
||||
BuyerLineItems_Struct bli{};
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(emu->payload),
|
||||
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(bli);
|
||||
|
||||
//packet size
|
||||
auto packet_size = bli.item_name.length() + 1 + 34;
|
||||
for (auto const &b: bli.trade_items) {
|
||||
packet_size += b.item_name.length() + 1;
|
||||
packet_size += 12;
|
||||
}
|
||||
|
||||
auto packet = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
|
||||
char *eq = (char *) packet->pBuffer;
|
||||
auto no_trade_items = bli.trade_items.size();
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSendBuyLine);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.slot);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.slot);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, bli.enabled ? 1 : 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_id);
|
||||
VARSTRUCT_ENCODE_STRING(eq, bli.item_name.c_str());
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_icon);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, bli.item_toggle ? 1 : 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_cost);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_trade_items);
|
||||
|
||||
for (auto const &i: bli.trade_items) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
|
||||
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
|
||||
}
|
||||
dest->QueuePacket(packet.get());
|
||||
safe_delete(inapp);
|
||||
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerSearch: {
|
||||
BuyerLineSearch_Struct bls{};
|
||||
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(emu->payload),
|
||||
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(bls);
|
||||
LogTrading("(RoF2) Barter_BuyerSearch action <green>[{}]", emu->action);
|
||||
|
||||
//Calculate size of packet
|
||||
auto p_size = 0;
|
||||
p_size += 5 * sizeof(uint32) + 1 * sizeof(uint8);
|
||||
p_size += bls.search_string.length() + 1;
|
||||
for (auto const &b: bls.buy_line) {
|
||||
p_size += 6 * sizeof(uint32) + 2 * sizeof(uint8);
|
||||
p_size += strlen(b.item_name) + 1;
|
||||
p_size += b.buyer_name.length() + 1;
|
||||
for (auto const &d: b.trade_items) {
|
||||
if (d.item_id != 0) {
|
||||
p_size += d.item_name.length() + 1;
|
||||
p_size += 3 * sizeof(uint32);
|
||||
}
|
||||
}
|
||||
p_size += 3 * sizeof(uint32);
|
||||
}
|
||||
|
||||
BuyerBuyLines_Struct bl{};
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, p_size);
|
||||
auto eq = (char *) outapp->pBuffer;
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 1);
|
||||
VARSTRUCT_ENCODE_STRING(eq, bls.search_string.c_str());
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bls.transaction_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, bls.no_items);
|
||||
for (auto const &b: bls.buy_line) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.slot);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_id);
|
||||
VARSTRUCT_ENCODE_STRING(eq, b.item_name);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_icon);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_cost);
|
||||
auto no_sub_items = b.trade_items.size();
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_sub_items);
|
||||
for (auto const &i: b.trade_items) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
|
||||
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
|
||||
}
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.buyer_entity_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.buyer_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_instance_id);
|
||||
VARSTRUCT_ENCODE_STRING(eq, b.buyer_name.c_str());
|
||||
}
|
||||
dest->QueuePacket(outapp.get());
|
||||
break;
|
||||
}
|
||||
case Barter_RemoveFromMerchantWindow: {
|
||||
auto emu = (BuyerRemoveItemFromMerchantWindow_Struct *) inapp->pBuffer;
|
||||
|
||||
emu->action = structs::RoF2BuyerActions::BuyerSendBuyLine;
|
||||
dest->FastQueuePacket(&inapp);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerTransactionComplete:
|
||||
case Barter_SellerTransactionComplete: {
|
||||
BuyerLineSellItem_Struct blsi{};
|
||||
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(emu->payload),
|
||||
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(blsi);
|
||||
|
||||
//packet size
|
||||
auto packet_size = strlen(blsi.item_name) * 2 + 2 + 48 + 30 + blsi.seller_name.length() + 1 +
|
||||
blsi.buyer_name.length() + 1;
|
||||
for (auto const &b: blsi.trade_items) {
|
||||
packet_size += b.item_name.length() + 1;
|
||||
packet_size += 12;
|
||||
}
|
||||
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
|
||||
auto eq = (char *) outapp->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case Barter_BuyerTransactionComplete: {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerBuyItem);
|
||||
break;
|
||||
}
|
||||
case Barter_SellerTransactionComplete: {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSellItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.sub_action);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.error_code);
|
||||
eq += 16;
|
||||
VARSTRUCT_ENCODE_STRING(eq, blsi.buyer_name.c_str());
|
||||
VARSTRUCT_ENCODE_STRING(eq, blsi.item_name);
|
||||
VARSTRUCT_ENCODE_STRING(eq, blsi.seller_name.c_str());
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFFFF);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFFFF);
|
||||
eq += 1;
|
||||
VARSTRUCT_ENCODE_STRING(eq, blsi.item_name);
|
||||
eq += 9;
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.item_cost);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.trade_items.size());
|
||||
|
||||
for (auto const &i: blsi.trade_items) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
|
||||
}
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFF);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.seller_quantity);
|
||||
|
||||
dest->QueuePacket(outapp.get());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dest->FastQueuePacket(&inapp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_CancelTrade)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
|
||||
@@ -1690,7 +1977,7 @@ namespace RoF2
|
||||
uchar *__emu_buffer = in->pBuffer;
|
||||
ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *) __emu_buffer;
|
||||
|
||||
switch(old_item_pkt->PacketType)
|
||||
switch(old_item_pkt->PacketType)
|
||||
{
|
||||
case ItemPacketParcel: {
|
||||
ParcelMessaging_Struct pms{};
|
||||
@@ -4348,6 +4635,9 @@ namespace RoF2
|
||||
if (emu->DestructibleObject) {
|
||||
OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData
|
||||
}
|
||||
if (emu->buyer) {
|
||||
OtherData = OtherData | 0x01;
|
||||
}
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
|
||||
// float EmitterScalingRadius
|
||||
@@ -4459,7 +4749,7 @@ namespace RoF2
|
||||
VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // TempPet
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId);
|
||||
@@ -4664,6 +4954,66 @@ namespace RoF2
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Barter)
|
||||
{
|
||||
auto action = *(uint32 *) __packet->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case structs::RoF2BuyerActions::BuyerRemoveItem: {
|
||||
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||
emu->action = Barter_BuyerItemRemove;
|
||||
LogTradingDetail("(RoF2) Buyer Remove Item");
|
||||
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BuyerActions::BuyerInspectBegin: {
|
||||
LogTradingDetail("(RoF2) Buyer Inspect Begin Item");
|
||||
|
||||
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||
emu->action = Barter_BuyerInspectBegin;
|
||||
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BuyerActions::BuyerInspectEnd: {
|
||||
LogTradingDetail("(RoF2) Buyer Inspect End Item ");
|
||||
|
||||
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||
emu->action = Barter_BuyerInspectEnd;
|
||||
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BuyerActions::BuyerWelcomeMessage: {
|
||||
LogTradingDetail("(RoF2) Buyer Welcome Message Update");
|
||||
SETUP_DIRECT_DECODE(BuyerWelcomeMessageUpdate_Struct, structs::BuyerWelcomeMessageUpdate_Struct);
|
||||
|
||||
emu->action = Barter_WelcomeMessageUpdate;
|
||||
strn0cpy(emu->welcome_message, eq->welcome_message, sizeof(emu->welcome_message));
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BuyerActions::BuyerItemInspect: {
|
||||
SETUP_DIRECT_DECODE(BarterItemSearchLinkRequest_Struct, structs::BarterItemSearchLinkRequest_Struct);
|
||||
LogTradingDetail("(RoF2) Seller ID <green>[{}] Inspecting Item <green>[{}] from Buyer ID <green>[{}] ",
|
||||
eq->seller_id,
|
||||
eq->item_id,
|
||||
eq->buyer_id
|
||||
);
|
||||
|
||||
emu->action = Barter_BarterItemInspect;
|
||||
emu->item_id = eq->item_id;
|
||||
emu->searcher_id = eq->seller_id;
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||
LogTradingDetail("(RoF2) Pass thru OP_Barter packet action <red>[{}]", emu->action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_BazaarSearch)
|
||||
{
|
||||
char *Buffer = (char *)__packet->pBuffer;
|
||||
@@ -4742,6 +5092,147 @@ namespace RoF2
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_BuyerItems)
|
||||
{
|
||||
auto action = *(uint32 *) __packet->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case structs::RoF2BuyerActions::BuyerModifyBuyLine:
|
||||
case structs::RoF2BuyerActions::BuyerBuyLine: {
|
||||
BuyerBuyLines_Struct buyer_buy_lines{};
|
||||
auto buffer = (char *) __packet->pBuffer;
|
||||
|
||||
buyer_buy_lines.action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
|
||||
buyer_buy_lines.no_items = 1;
|
||||
if (action == structs::RoF2BuyerActions::BuyerBuyLine) {
|
||||
buyer_buy_lines.no_items = VARSTRUCT_DECODE_TYPE(uint16, buffer);
|
||||
}
|
||||
|
||||
buyer_buy_lines.buy_lines.reserve(buyer_buy_lines.no_items);
|
||||
for (int i = 0; i < buyer_buy_lines.no_items; i++) {
|
||||
BuyerLineItems_Struct b{};
|
||||
b.slot = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
b.enabled = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||
b.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
b.item_name = std::string(buffer, strlen(buffer));
|
||||
buffer += strlen(buffer) + 1;
|
||||
b.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
b.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
b.item_toggle = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||
b.item_cost = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
auto trade_items = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
buyer_buy_lines.buy_lines.push_back(b);
|
||||
|
||||
if (trade_items > 0) {
|
||||
buyer_buy_lines.buy_lines[i].trade_items.reserve(trade_items);
|
||||
for (int x = 0; x < trade_items; x++) {
|
||||
BuyerLineTradeItems_Struct blti{};
|
||||
blti.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
blti.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
blti.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
blti.item_name = std::string(buffer, strlen(buffer));
|
||||
buffer += strlen(buffer) + 1;
|
||||
buyer_buy_lines.buy_lines[i].trade_items.push_back(blti);
|
||||
}
|
||||
}
|
||||
buffer += 13;
|
||||
}
|
||||
|
||||
buffer = nullptr;
|
||||
std::stringstream ss{};
|
||||
cereal::BinaryOutputArchive ar(ss);
|
||||
{
|
||||
ar(buyer_buy_lines);
|
||||
}
|
||||
|
||||
auto new_size = sizeof(BuyerGeneric_Struct) + ss.str().length();
|
||||
auto new_packet = new unsigned char[new_size];
|
||||
__packet->size = new_size;
|
||||
__packet->pBuffer = new_packet;
|
||||
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||
emu->action = Barter_BuyerItemUpdate;
|
||||
|
||||
if (action == structs::RoF2BuyerActions::BuyerBuyLine) {
|
||||
emu->action = Barter_BuyerItemStart;
|
||||
}
|
||||
|
||||
memcpy(emu->payload, ss.str().data(), ss.str().length());
|
||||
__packet->SetOpcode(OP_Barter);
|
||||
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BuyerActions::BuyerSellItem: {
|
||||
BuyerLineSellItem_Struct sell_item{};
|
||||
|
||||
char *buffer = (char *) __packet->pBuffer;
|
||||
|
||||
sell_item.action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.purchase_method = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
buffer += 4;
|
||||
sell_item.buyer_entity_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.buyer_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
buffer += 11;
|
||||
sell_item.slot = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.enabled = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||
sell_item.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
VARSTRUCT_DECODE_STRING(sell_item.item_name, buffer);
|
||||
sell_item.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.item_toggle = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||
sell_item.item_cost = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.no_trade_items = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
|
||||
if (sell_item.no_trade_items > 0) {
|
||||
sell_item.trade_items.reserve(sell_item.no_trade_items);
|
||||
for (int x = 0; x < sell_item.no_trade_items; x++) {
|
||||
BuyerLineTradeItems_Struct blti{};
|
||||
blti.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
blti.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
blti.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
blti.item_name = std::string(buffer, strlen(buffer));
|
||||
buffer += strlen(buffer) + 1;
|
||||
sell_item.trade_items.push_back(blti);
|
||||
}
|
||||
}
|
||||
|
||||
if (sell_item.purchase_method) {
|
||||
sell_item.buyer_entity_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.buyer_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.zone_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
sell_item.buyer_name = std::string(buffer, strlen(buffer));
|
||||
buffer += sell_item.buyer_name.length() + 1;
|
||||
}
|
||||
else {
|
||||
buffer += 13;
|
||||
}
|
||||
|
||||
sell_item.seller_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||
|
||||
buffer += 4;
|
||||
|
||||
buffer = nullptr;
|
||||
std::stringstream ss{};
|
||||
cereal::BinaryOutputArchive ar(ss);
|
||||
{
|
||||
ar(sell_item);
|
||||
}
|
||||
|
||||
auto new_size = sizeof(BuyerGeneric_Struct) + ss.str().length();
|
||||
auto new_packet = new unsigned char[new_size];
|
||||
__packet->size = new_size;
|
||||
__packet->pBuffer = new_packet;
|
||||
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||
emu->action = Barter_SellItem;
|
||||
|
||||
memcpy(emu->payload, ss.str().data(), ss.str().length());
|
||||
__packet->SetOpcode(OP_Barter);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_CastSpell)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
|
||||
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -37,7 +37,7 @@ namespace RoF2
|
||||
|
||||
const bool AllowOverLevelEquipment = true;
|
||||
|
||||
const bool AllowEmptyBagInBag = true;
|
||||
const bool AllowEmptyBagInBag = true;
|
||||
const bool AllowClickCastFromBag = true;
|
||||
|
||||
} /*inventory*/
|
||||
@@ -77,38 +77,38 @@ namespace RoF2
|
||||
} // namespace enum_
|
||||
using namespace enum_;
|
||||
|
||||
const int16 POSSESSIONS_SIZE = 34;
|
||||
const int16 BANK_SIZE = 24;
|
||||
const int16 SHARED_BANK_SIZE = 2;
|
||||
const int16 TRADE_SIZE = 8;
|
||||
const int16 WORLD_SIZE = 10;
|
||||
const int16 LIMBO_SIZE = 36;
|
||||
const int16 TRIBUTE_SIZE = 5;
|
||||
const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown
|
||||
const int16 GUILD_TRIBUTE_SIZE = 2;//unverified
|
||||
const int16 MERCHANT_SIZE = 200;
|
||||
const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab"
|
||||
const int16 CORPSE_SIZE = POSSESSIONS_SIZE;
|
||||
const int16 BAZAAR_SIZE = 200;
|
||||
const int16 INSPECT_SIZE = 23;
|
||||
const int16 REAL_ESTATE_SIZE = 0;//unknown
|
||||
const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE;
|
||||
const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE;
|
||||
const int16 POSSESSIONS_SIZE = 34;
|
||||
const int16 BANK_SIZE = 24;
|
||||
const int16 SHARED_BANK_SIZE = 2;
|
||||
const int16 TRADE_SIZE = 8;
|
||||
const int16 WORLD_SIZE = 10;
|
||||
const int16 LIMBO_SIZE = 36;
|
||||
const int16 TRIBUTE_SIZE = 5;
|
||||
const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown
|
||||
const int16 GUILD_TRIBUTE_SIZE = 2;//unverified
|
||||
const int16 MERCHANT_SIZE = 500;
|
||||
const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab"
|
||||
const int16 CORPSE_SIZE = POSSESSIONS_SIZE;
|
||||
const int16 BAZAAR_SIZE = 200;
|
||||
const int16 INSPECT_SIZE = 23;
|
||||
const int16 REAL_ESTATE_SIZE = 0;//unknown
|
||||
const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE;
|
||||
const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE;
|
||||
const int16 VIEW_MOD_SHARED_BANK_SIZE = SHARED_BANK_SIZE;
|
||||
const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE;
|
||||
const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank"
|
||||
const int16 ARCHIVED_SIZE = 0;//unknown
|
||||
const int16 MAIL_SIZE = 0;//unknown
|
||||
const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE;
|
||||
const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank"
|
||||
const int16 ARCHIVED_SIZE = 0;//unknown
|
||||
const int16 MAIL_SIZE = 0;//unknown
|
||||
const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown
|
||||
const int16 KRONO_SIZE = 0;//unknown
|
||||
const int16 OTHER_SIZE = 0;//unknown
|
||||
const int16 KRONO_SIZE = 0;//unknown
|
||||
const int16 OTHER_SIZE = 0;//unknown
|
||||
|
||||
const int16 TRADE_NPC_SIZE = 4; // defined by implication
|
||||
|
||||
const int16 TYPE_INVALID = IINVALID;
|
||||
const int16 TYPE_BEGIN = typePossessions;
|
||||
const int16 TYPE_END = typeOther;
|
||||
const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1;
|
||||
const int16 TYPE_BEGIN = typePossessions;
|
||||
const int16 TYPE_END = typeOther;
|
||||
const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1;
|
||||
|
||||
int16 GetInvTypeSize(int16 inv_type);
|
||||
const char* GetInvTypeName(int16 inv_type);
|
||||
@@ -162,33 +162,54 @@ namespace RoF2
|
||||
} // namespace enum_
|
||||
using namespace enum_;
|
||||
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
|
||||
const int16 BANK_BEGIN = 2000;
|
||||
const int16 BANK_END = (BANK_BEGIN + invtype::BANK_SIZE) - 1;
|
||||
|
||||
const int16 SHARED_BANK_BEGIN = 2500;
|
||||
const int16 SHARED_BANK_END = (SHARED_BANK_BEGIN + invtype::SHARED_BANK_SIZE) - 1;
|
||||
|
||||
const int16 TRADE_BEGIN = 3000;
|
||||
const int16 TRADE_END = (TRADE_BEGIN + invtype::TRADE_SIZE) - 1;
|
||||
|
||||
const int16 TRADE_NPC_END = (TRADE_BEGIN + invtype::TRADE_NPC_SIZE) - 1; // defined by implication
|
||||
|
||||
const int16 WORLD_BEGIN = 4000;
|
||||
const int16 WORLD_END = (WORLD_BEGIN + invtype::WORLD_SIZE) - 1;
|
||||
|
||||
const int16 TRIBUTE_BEGIN = 400;
|
||||
const int16 TRIBUTE_END = (TRIBUTE_BEGIN + invtype::TRIBUTE_SIZE) - 1;
|
||||
|
||||
const int16 GUILD_TRIBUTE_BEGIN = 450;
|
||||
const int16 GUILD_TRIBUTE_END = (GUILD_TRIBUTE_BEGIN + invtype::GUILD_TRIBUTE_SIZE) - 1;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
const int16 EQUIPMENT_BEGIN = slotCharm;
|
||||
const int16 EQUIPMENT_END = slotAmmo;
|
||||
const int16 EQUIPMENT_END = slotAmmo;
|
||||
const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1;
|
||||
|
||||
const int16 GENERAL_BEGIN = slotGeneral1;
|
||||
const int16 GENERAL_END = slotGeneral10;
|
||||
const int16 GENERAL_END = slotGeneral10;
|
||||
const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1;
|
||||
|
||||
const int16 BONUS_BEGIN = invslot::slotCharm;
|
||||
const int16 BONUS_STAT_END = invslot::slotPowerSource;
|
||||
const int16 BONUS_BEGIN = invslot::slotCharm;
|
||||
const int16 BONUS_STAT_END = invslot::slotPowerSource;
|
||||
const int16 BONUS_SKILL_END = invslot::slotAmmo;
|
||||
|
||||
const int16 CORPSE_BEGIN = invslot::slotGeneral1;
|
||||
const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor;
|
||||
const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor;
|
||||
|
||||
const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF;
|
||||
const uint64 GENERAL_BITMASK = 0x00000001FF800000;
|
||||
const uint64 CURSOR_BITMASK = 0x0000000200000000;
|
||||
const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF;
|
||||
const uint64 GENERAL_BITMASK = 0x00000001FF800000;
|
||||
const uint64 CURSOR_BITMASK = 0x0000000200000000;
|
||||
const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 34-slot count (RoF+)
|
||||
const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+)
|
||||
const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+)
|
||||
|
||||
|
||||
const char* GetInvPossessionsSlotName(int16 inv_slot);
|
||||
@@ -199,10 +220,21 @@ namespace RoF2
|
||||
namespace invbag {
|
||||
inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::RoF2; }
|
||||
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
const int16 SLOT_END = 9; //254;
|
||||
const int16 SLOT_COUNT = 10; //255; // server Size will be 255..unsure what actual client is (test)
|
||||
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
const int16 SLOT_COUNT = 200;
|
||||
const int16 SLOT_END = SLOT_COUNT - 1;
|
||||
|
||||
const int16 GENERAL_BAGS_BEGIN = 251;
|
||||
|
||||
const int16 CURSOR_BAG_BEGIN = 351;
|
||||
|
||||
const int16 BANK_BAGS_BEGIN = 2031;
|
||||
|
||||
const int16 SHARED_BANK_BAGS_BEGIN = 2531;
|
||||
|
||||
const int16 TRADE_BAGS_BEGIN = 3031;
|
||||
|
||||
const char* GetInvBagIndexName(int16 bag_index);
|
||||
|
||||
@@ -212,9 +244,9 @@ namespace RoF2
|
||||
inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::RoF2; }
|
||||
|
||||
const int16 SOCKET_INVALID = IINVALID;
|
||||
const int16 SOCKET_BEGIN = INULL;
|
||||
const int16 SOCKET_END = 5;
|
||||
const int16 SOCKET_COUNT = 6;
|
||||
const int16 SOCKET_BEGIN = INULL;
|
||||
const int16 SOCKET_END = 5;
|
||||
const int16 SOCKET_COUNT = 6;
|
||||
|
||||
const char* GetInvAugIndexName(int16 aug_index);
|
||||
|
||||
@@ -291,7 +323,7 @@ namespace RoF2
|
||||
|
||||
namespace spells {
|
||||
inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::RoF2; }
|
||||
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
@@ -314,7 +346,7 @@ namespace RoF2
|
||||
const int SPELL_ID_MAX = 45000;
|
||||
const int SPELLBOOK_SIZE = 720;
|
||||
const int SPELL_GEM_COUNT = static_cast<uint32>(CastingSlot::MaxGems);
|
||||
|
||||
|
||||
const int LONG_BUFFS = 42;
|
||||
const int SHORT_BUFFS = 20;
|
||||
const int DISC_BUFFS = 1;
|
||||
|
||||
@@ -48,6 +48,7 @@ E(OP_BeginCast)
|
||||
E(OP_BlockedBuffs)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffCreate)
|
||||
E(OP_BuyerItems)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_CastSpell)
|
||||
E(OP_ChannelMessage)
|
||||
@@ -149,11 +150,13 @@ D(OP_Animation)
|
||||
D(OP_ApplyPoison)
|
||||
D(OP_AugmentInfo)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_Barter)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_BlockedBuffs)
|
||||
D(OP_BookButton)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffRemoveRequest)
|
||||
D(OP_BuyerItems)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
D(OP_CharacterCreate)
|
||||
|
||||
+142
-22
@@ -354,15 +354,15 @@ struct Spawn_Struct_Bitfields
|
||||
/*29*/ unsigned showname:1;
|
||||
/*30*/ unsigned idleanimationsoff:1; // what we called statue?
|
||||
/*31*/ unsigned untargetable:1; // bClickThrough
|
||||
/* do these later
|
||||
32 unsigned buyer:1;
|
||||
33 unsigned offline:1;
|
||||
34 unsigned interactiveobject:1;
|
||||
35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
|
||||
36 unsigned title:1;
|
||||
37 unsigned suffix:1;
|
||||
38 unsigned padding1:1;
|
||||
39 unsigned padding2:1;
|
||||
// byte 5
|
||||
/*32 unsigned buyer:1;
|
||||
/*33 unsigned offline:1;
|
||||
/*34 unsigned interactiveobject:1;
|
||||
/*35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
|
||||
/*36 unsigned title:1;
|
||||
/*37 unsigned suffix:1;
|
||||
/*38 unsigned padding1:1;
|
||||
/*39 unsigned padding2:1;
|
||||
40 unsinged padding3:1;
|
||||
*/
|
||||
/*
|
||||
@@ -3107,19 +3107,139 @@ struct EnvDamage2_Struct {
|
||||
|
||||
//Bazaar Stuff
|
||||
enum RoF2BazaarTraderBuyerActions {
|
||||
Zero = 0,
|
||||
BeginTraderMode = 1,
|
||||
EndTraderMode = 2,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
BazaarInspect = 18,
|
||||
ClickTrader = 28,
|
||||
ItemMove = 19,
|
||||
ReconcileItems = 20
|
||||
Zero = 0,
|
||||
BeginTraderMode = 1,
|
||||
EndTraderMode = 2,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
BazaarInspect = 18,
|
||||
ClickTrader = 28,
|
||||
ItemMove = 19,
|
||||
ReconcileItems = 20
|
||||
};
|
||||
|
||||
enum RoF2BuyerActions {
|
||||
BuyerSearchResults = 0x00,
|
||||
BuyerBuyLine = 0x06,
|
||||
BuyerModifyBuyLine = 0x07,
|
||||
BuyerRemoveItem = 0x08,
|
||||
BuyerSellItem = 0x09,
|
||||
BuyerBuyItem = 0x0a,
|
||||
BuyerInspectBegin = 0x0b,
|
||||
BuyerInspectEnd = 0x0c,
|
||||
BuyerAppearance = 0x0d,
|
||||
BuyerSendBuyLine = 0x0e,
|
||||
BuyerItemInspect = 0x0f,
|
||||
BuyerBrowsingBuyLine = 0x10,
|
||||
BarterWelcomeMessage = 0x11,
|
||||
BuyerWelcomeMessage = 0x13,
|
||||
BuyerGreeting = 0x14,
|
||||
BuyerInventoryFull = 0x16
|
||||
};
|
||||
|
||||
struct BarterItemSearchLinkRequest_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 seller_id;
|
||||
uint32 buyer_id;
|
||||
uint32 unknown_016;
|
||||
uint32 slot_id; // 0xffffffff main buy line 0x0 trade_item_1, 0x1 trade_item_2
|
||||
uint32 item_id;
|
||||
uint32 unknown_028;
|
||||
};
|
||||
|
||||
struct BuyerWelcomeMessageUpdate_Struct {
|
||||
uint32 action;
|
||||
char unknown_004[64];
|
||||
uint32 unknown_068;
|
||||
char welcome_message[256];
|
||||
};
|
||||
|
||||
struct Buyer_SetAppearance_Struct {
|
||||
uint32 action;
|
||||
uint32 entity_id;
|
||||
char unknown[64];
|
||||
uint32 enabled;
|
||||
};
|
||||
|
||||
struct BuyerRemoveItem_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown004;
|
||||
uint32 slot_id;
|
||||
uint32 toggle;
|
||||
};
|
||||
|
||||
struct BuyerLineSellItem_Struct {
|
||||
uint32 action;
|
||||
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
|
||||
uint32 unknown008;
|
||||
uint32 buyer_entity_id;
|
||||
uint32 seller_entity_id;
|
||||
char unknown[15];
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
char item_name[64];
|
||||
uint32 item_icon;
|
||||
uint32 item_quantity;
|
||||
uint8 item_toggle;
|
||||
uint32 item_cost;
|
||||
uint32 no_trade_items;
|
||||
BuyerLineTradeItems_Struct trade_items[10];
|
||||
char unknown2[13];
|
||||
uint32 seller_quantity;
|
||||
};
|
||||
|
||||
struct BuyerLineItemsSearch_Struct {
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
char item_name[64];
|
||||
uint32 item_icon;
|
||||
uint32 item_quantity;
|
||||
uint8 item_toggle;
|
||||
uint32 item_cost;
|
||||
uint32 buyer_id;
|
||||
BuyerLineTradeItems_Struct trade_items[MAX_BUYER_COMPENSATION_ITEMS];
|
||||
};
|
||||
|
||||
struct BuyerLineSearch_Struct {
|
||||
uint32 action;
|
||||
uint32 no_items;
|
||||
std::vector<BuyerLineItemsSearch_Struct> buy_line;
|
||||
};
|
||||
|
||||
struct BuyerStart_Struct {
|
||||
uint32 action;
|
||||
uint16 no_buyer_lines;
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
char item_name[1]; // vary length
|
||||
uint32 item_icon;
|
||||
uint32 item_quantity;
|
||||
uint8 toggle;
|
||||
uint32 item_cost;
|
||||
uint32 no_trade_items;
|
||||
BuyerLineTradeItems_Struct trade_items[1]; // size is actually no_trade_items. If 0, then this is not in packet
|
||||
char unknown[13];
|
||||
};
|
||||
|
||||
struct BuyerItemSearchResultEntry_Struct {
|
||||
char item_name[64];
|
||||
uint32 item_id;
|
||||
uint32 item_icon;
|
||||
uint32 unknown_072;
|
||||
};
|
||||
|
||||
struct BuyerItemSearchResults_Struct {
|
||||
uint32 action;
|
||||
uint32 result_count;
|
||||
BuyerItemSearchResultEntry_Struct results[];
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -2463,25 +2463,25 @@ struct WhoAllReturnStruct {
|
||||
struct BeginTrader_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown04;
|
||||
uint64 serial_number[80];
|
||||
uint32 cost[80];
|
||||
uint64 serial_number[EQ::invtype::BAZAAR_SIZE];
|
||||
uint32 cost[EQ::invtype::BAZAAR_SIZE];
|
||||
};
|
||||
|
||||
struct Trader_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown004;
|
||||
uint64 item_id[80];
|
||||
uint32 item_cost[80];
|
||||
uint64 item_id[EQ::invtype::BAZAAR_SIZE];
|
||||
uint32 item_cost[EQ::invtype::BAZAAR_SIZE];
|
||||
};
|
||||
|
||||
struct ClickTrader_Struct {
|
||||
uint32 code;
|
||||
uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE
|
||||
uint32 itemcost[80];
|
||||
uint32 itemcost[EQ::invtype::BAZAAR_SIZE];
|
||||
};
|
||||
|
||||
struct GetItems_Struct{
|
||||
uint32 items[80];
|
||||
uint32 items[EQ::invtype::BAZAAR_SIZE];
|
||||
};
|
||||
|
||||
struct BecomeTrader_Struct {
|
||||
|
||||
@@ -74,6 +74,11 @@ void PathManager::LoadPaths()
|
||||
m_patch_path = fs::relative(fs::path{m_server_path + "/" + c->PatchDir}).string();
|
||||
}
|
||||
|
||||
// patches
|
||||
if (File::Exists(fs::path{ m_server_path + "/" + c->OpcodeDir }.string())) {
|
||||
m_opcode_path = fs::relative(fs::path{ m_server_path + "/" + c->OpcodeDir }).string();
|
||||
}
|
||||
|
||||
// shared_memory_path
|
||||
if (File::Exists(fs::path{m_server_path + "/" + c->SharedMemDir}.string())) {
|
||||
m_shared_memory_path = fs::relative(fs::path{ m_server_path + "/" + c->SharedMemDir }).string();
|
||||
@@ -89,6 +94,7 @@ void PathManager::LoadPaths()
|
||||
LogInfo("lua_modules path [{}]", m_lua_modules_path);
|
||||
LogInfo("maps path [{}]", m_maps_path);
|
||||
LogInfo("patches path [{}]", m_patch_path);
|
||||
LogInfo("opcode path [{}]", m_opcode_path);
|
||||
LogInfo("plugins path [{}]", m_plugins_path);
|
||||
LogInfo("quests path [{}]", m_quests_path);
|
||||
LogInfo("shared_memory path [{}]", m_shared_memory_path);
|
||||
@@ -129,6 +135,11 @@ const std::string &PathManager::GetPatchPath() const
|
||||
return m_patch_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetOpcodePath() const
|
||||
{
|
||||
return m_opcode_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetLuaModulesPath() const
|
||||
{
|
||||
return m_lua_modules_path;
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
[[nodiscard]] const std::string &GetLuaModulesPath() const;
|
||||
[[nodiscard]] const std::string &GetMapsPath() const;
|
||||
[[nodiscard]] const std::string &GetPatchPath() const;
|
||||
[[nodiscard]] const std::string &GetOpcodePath() const;
|
||||
[[nodiscard]] const std::string &GetPluginsPath() const;
|
||||
[[nodiscard]] const std::string &GetQuestsPath() const;
|
||||
[[nodiscard]] const std::string &GetServerPath() const;
|
||||
@@ -24,6 +25,7 @@ private:
|
||||
std::string m_lua_modules_path;
|
||||
std::string m_maps_path;
|
||||
std::string m_patch_path;
|
||||
std::string m_opcode_path;
|
||||
std::string m_plugins_path;
|
||||
std::string m_quests_path;
|
||||
std::string m_server_path;
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
struct BotSpellsEntries {
|
||||
uint32_t id;
|
||||
int32_t npc_spells_id;
|
||||
int16_t spellid;
|
||||
uint16_t spell_id;
|
||||
uint32_t type;
|
||||
uint8_t minlevel;
|
||||
uint8_t maxlevel;
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
return {
|
||||
"id",
|
||||
"npc_spells_id",
|
||||
"spellid",
|
||||
"spell_id",
|
||||
"type",
|
||||
"minlevel",
|
||||
"maxlevel",
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
return {
|
||||
"id",
|
||||
"npc_spells_id",
|
||||
"spellid",
|
||||
"spell_id",
|
||||
"type",
|
||||
"minlevel",
|
||||
"maxlevel",
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
|
||||
e.id = 0;
|
||||
e.npc_spells_id = 0;
|
||||
e.spellid = 0;
|
||||
e.spell_id = 0;
|
||||
e.type = 0;
|
||||
e.minlevel = 0;
|
||||
e.maxlevel = 255;
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0;
|
||||
e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.npc_spells_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.spellid));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.spell_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.type));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.minlevel));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.maxlevel));
|
||||
@@ -256,7 +256,7 @@ public:
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.minlevel));
|
||||
v.push_back(std::to_string(e.maxlevel));
|
||||
@@ -300,7 +300,7 @@ public:
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.minlevel));
|
||||
v.push_back(std::to_string(e.maxlevel));
|
||||
@@ -348,7 +348,7 @@ public:
|
||||
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0;
|
||||
e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
||||
@@ -387,7 +387,7 @@ public:
|
||||
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0;
|
||||
e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
||||
@@ -476,7 +476,7 @@ public:
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.minlevel));
|
||||
v.push_back(std::to_string(e.maxlevel));
|
||||
@@ -513,7 +513,7 @@ public:
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.minlevel));
|
||||
v.push_back(std::to_string(e.maxlevel));
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/**
|
||||
* 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_BUYER_BUY_LINES_REPOSITORY_H
|
||||
#define EQEMU_BASE_BUYER_BUY_LINES_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseBuyerBuyLinesRepository {
|
||||
public:
|
||||
struct BuyerBuyLines {
|
||||
uint64_t id;
|
||||
uint64_t buyer_id;
|
||||
uint32_t char_id;
|
||||
int32_t buy_slot_id;
|
||||
int32_t item_id;
|
||||
int32_t item_qty;
|
||||
int32_t item_price;
|
||||
uint32_t item_icon;
|
||||
std::string item_name;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"buyer_id",
|
||||
"char_id",
|
||||
"buy_slot_id",
|
||||
"item_id",
|
||||
"item_qty",
|
||||
"item_price",
|
||||
"item_icon",
|
||||
"item_name",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"buyer_id",
|
||||
"char_id",
|
||||
"buy_slot_id",
|
||||
"item_id",
|
||||
"item_qty",
|
||||
"item_price",
|
||||
"item_icon",
|
||||
"item_name",
|
||||
};
|
||||
}
|
||||
|
||||
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("buyer_buy_lines");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static BuyerBuyLines NewEntity()
|
||||
{
|
||||
BuyerBuyLines e{};
|
||||
|
||||
e.id = 0;
|
||||
e.buyer_id = 0;
|
||||
e.char_id = 0;
|
||||
e.buy_slot_id = 0;
|
||||
e.item_id = 0;
|
||||
e.item_qty = 0;
|
||||
e.item_price = 0;
|
||||
e.item_icon = 0;
|
||||
e.item_name = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static BuyerBuyLines GetBuyerBuyLines(
|
||||
const std::vector<BuyerBuyLines> &buyer_buy_liness,
|
||||
int buyer_buy_lines_id
|
||||
)
|
||||
{
|
||||
for (auto &buyer_buy_lines : buyer_buy_liness) {
|
||||
if (buyer_buy_lines.id == buyer_buy_lines_id) {
|
||||
return buyer_buy_lines;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static BuyerBuyLines FindOne(
|
||||
Database& db,
|
||||
int buyer_buy_lines_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
buyer_buy_lines_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
BuyerBuyLines e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
|
||||
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.item_name = row[8] ? row[8] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int buyer_buy_lines_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
buyer_buy_lines_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const BuyerBuyLines &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.buyer_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.char_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.buy_slot_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.item_qty));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.item_price));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.item_icon));
|
||||
v.push_back(columns[8] + " = '" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static BuyerBuyLines InsertOne(
|
||||
Database& db,
|
||||
BuyerBuyLines e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.buyer_id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.buy_slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_price));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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<BuyerBuyLines> &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.buyer_id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.buy_slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_price));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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<BuyerBuyLines> All(Database& db)
|
||||
{
|
||||
std::vector<BuyerBuyLines> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BuyerBuyLines e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
|
||||
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.item_name = row[8] ? row[8] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BuyerBuyLines> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<BuyerBuyLines> 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) {
|
||||
BuyerBuyLines e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
|
||||
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.item_name = row[8] ? row[8] : "";
|
||||
|
||||
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 BuyerBuyLines &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.buyer_id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.buy_slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_price));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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<BuyerBuyLines> &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.buyer_id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.buy_slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_price));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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_BUYER_BUY_LINES_REPOSITORY_H
|
||||
@@ -19,40 +19,46 @@
|
||||
class BaseBuyerRepository {
|
||||
public:
|
||||
struct Buyer {
|
||||
int32_t charid;
|
||||
int32_t buyslot;
|
||||
int32_t itemid;
|
||||
std::string itemname;
|
||||
int32_t quantity;
|
||||
int32_t price;
|
||||
uint64_t id;
|
||||
uint32_t char_id;
|
||||
uint32_t char_entity_id;
|
||||
std::string char_name;
|
||||
uint32_t char_zone_id;
|
||||
uint32_t char_zone_instance_id;
|
||||
time_t transaction_date;
|
||||
std::string welcome_message;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("charid");
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"charid",
|
||||
"buyslot",
|
||||
"itemid",
|
||||
"itemname",
|
||||
"quantity",
|
||||
"price",
|
||||
"id",
|
||||
"char_id",
|
||||
"char_entity_id",
|
||||
"char_name",
|
||||
"char_zone_id",
|
||||
"char_zone_instance_id",
|
||||
"transaction_date",
|
||||
"welcome_message",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"charid",
|
||||
"buyslot",
|
||||
"itemid",
|
||||
"itemname",
|
||||
"quantity",
|
||||
"price",
|
||||
"id",
|
||||
"char_id",
|
||||
"char_entity_id",
|
||||
"char_name",
|
||||
"char_zone_id",
|
||||
"char_zone_instance_id",
|
||||
"UNIX_TIMESTAMP(transaction_date)",
|
||||
"welcome_message",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,12 +99,14 @@ public:
|
||||
{
|
||||
Buyer e{};
|
||||
|
||||
e.charid = 0;
|
||||
e.buyslot = 0;
|
||||
e.itemid = 0;
|
||||
e.itemname = "";
|
||||
e.quantity = 0;
|
||||
e.price = 0;
|
||||
e.id = 0;
|
||||
e.char_id = 0;
|
||||
e.char_entity_id = 0;
|
||||
e.char_name = "";
|
||||
e.char_zone_id = 0;
|
||||
e.char_zone_instance_id = 0;
|
||||
e.transaction_date = 0;
|
||||
e.welcome_message = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -109,7 +117,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &buyer : buyers) {
|
||||
if (buyer.charid == buyer_id) {
|
||||
if (buyer.id == buyer_id) {
|
||||
return buyer;
|
||||
}
|
||||
}
|
||||
@@ -135,12 +143,14 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
Buyer e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.itemname = row[3] ? row[3] : "";
|
||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.char_name = row[3] ? row[3] : "";
|
||||
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
|
||||
e.welcome_message = row[7] ? row[7] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -174,12 +184,13 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.charid));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.buyslot));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.itemid));
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.quantity));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.price));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.char_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.char_entity_id));
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.char_zone_id));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.char_zone_instance_id));
|
||||
v.push_back(columns[6] + " = FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.welcome_message) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -187,7 +198,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.charid
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -201,12 +212,14 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -217,7 +230,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.charid = results.LastInsertedID();
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -236,12 +249,14 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -275,12 +290,14 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Buyer e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.itemname = row[3] ? row[3] : "";
|
||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.char_name = row[3] ? row[3] : "";
|
||||
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
|
||||
e.welcome_message = row[7] ? row[7] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -305,12 +322,14 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Buyer e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.itemname = row[3] ? row[3] : "";
|
||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.char_name = row[3] ? row[3] : "";
|
||||
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
|
||||
e.welcome_message = row[7] ? row[7] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -385,12 +404,14 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -413,12 +434,14 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,439 @@
|
||||
/**
|
||||
* 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_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||
#define EQEMU_BASE_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseBuyerTradeItemsRepository {
|
||||
public:
|
||||
struct BuyerTradeItems {
|
||||
uint64_t id;
|
||||
uint64_t buyer_buy_lines_id;
|
||||
int32_t item_id;
|
||||
int32_t item_qty;
|
||||
int32_t item_icon;
|
||||
std::string item_name;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"buyer_buy_lines_id",
|
||||
"item_id",
|
||||
"item_qty",
|
||||
"item_icon",
|
||||
"item_name",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"buyer_buy_lines_id",
|
||||
"item_id",
|
||||
"item_qty",
|
||||
"item_icon",
|
||||
"item_name",
|
||||
};
|
||||
}
|
||||
|
||||
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("buyer_trade_items");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static BuyerTradeItems NewEntity()
|
||||
{
|
||||
BuyerTradeItems e{};
|
||||
|
||||
e.id = 0;
|
||||
e.buyer_buy_lines_id = 0;
|
||||
e.item_id = 0;
|
||||
e.item_qty = 0;
|
||||
e.item_icon = 0;
|
||||
e.item_name = "0";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static BuyerTradeItems GetBuyerTradeItems(
|
||||
const std::vector<BuyerTradeItems> &buyer_trade_itemss,
|
||||
int buyer_trade_items_id
|
||||
)
|
||||
{
|
||||
for (auto &buyer_trade_items : buyer_trade_itemss) {
|
||||
if (buyer_trade_items.id == buyer_trade_items_id) {
|
||||
return buyer_trade_items;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static BuyerTradeItems FindOne(
|
||||
Database& db,
|
||||
int buyer_trade_items_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
buyer_trade_items_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
BuyerTradeItems e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_name = row[5] ? row[5] : "0";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int buyer_trade_items_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
buyer_trade_items_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const BuyerTradeItems &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.buyer_buy_lines_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.item_qty));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.item_icon));
|
||||
v.push_back(columns[5] + " = '" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static BuyerTradeItems InsertOne(
|
||||
Database& db,
|
||||
BuyerTradeItems e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.buyer_buy_lines_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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<BuyerTradeItems> &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.buyer_buy_lines_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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<BuyerTradeItems> All(Database& db)
|
||||
{
|
||||
std::vector<BuyerTradeItems> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BuyerTradeItems e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_name = row[5] ? row[5] : "0";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BuyerTradeItems> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<BuyerTradeItems> 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) {
|
||||
BuyerTradeItems e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_name = row[5] ? row[5] : "0";
|
||||
|
||||
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 BuyerTradeItems &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.buyer_buy_lines_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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<BuyerTradeItems> &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.buyer_buy_lines_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.item_qty));
|
||||
v.push_back(std::to_string(e.item_icon));
|
||||
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||
|
||||
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_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||
@@ -19,70 +19,73 @@
|
||||
class BaseInventoryRepository {
|
||||
public:
|
||||
struct Inventory {
|
||||
uint32_t charid;
|
||||
uint32_t slotid;
|
||||
uint32_t itemid;
|
||||
uint32_t character_id;
|
||||
uint32_t slot_id;
|
||||
uint32_t item_id;
|
||||
uint16_t charges;
|
||||
uint32_t color;
|
||||
uint32_t augslot1;
|
||||
uint32_t augslot2;
|
||||
uint32_t augslot3;
|
||||
uint32_t augslot4;
|
||||
uint32_t augslot5;
|
||||
int32_t augslot6;
|
||||
uint32_t augment_one;
|
||||
uint32_t augment_two;
|
||||
uint32_t augment_three;
|
||||
uint32_t augment_four;
|
||||
uint32_t augment_five;
|
||||
uint32_t augment_six;
|
||||
uint8_t instnodrop;
|
||||
std::string custom_data;
|
||||
uint32_t ornamenticon;
|
||||
uint32_t ornamentidfile;
|
||||
uint32_t ornament_icon;
|
||||
uint32_t ornament_idfile;
|
||||
int32_t ornament_hero_model;
|
||||
uint64_t guid;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("charid");
|
||||
return std::string("character_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"charid",
|
||||
"slotid",
|
||||
"itemid",
|
||||
"character_id",
|
||||
"slot_id",
|
||||
"item_id",
|
||||
"charges",
|
||||
"color",
|
||||
"augslot1",
|
||||
"augslot2",
|
||||
"augslot3",
|
||||
"augslot4",
|
||||
"augslot5",
|
||||
"augslot6",
|
||||
"augment_one",
|
||||
"augment_two",
|
||||
"augment_three",
|
||||
"augment_four",
|
||||
"augment_five",
|
||||
"augment_six",
|
||||
"instnodrop",
|
||||
"custom_data",
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_icon",
|
||||
"ornament_idfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"charid",
|
||||
"slotid",
|
||||
"itemid",
|
||||
"character_id",
|
||||
"slot_id",
|
||||
"item_id",
|
||||
"charges",
|
||||
"color",
|
||||
"augslot1",
|
||||
"augslot2",
|
||||
"augslot3",
|
||||
"augslot4",
|
||||
"augslot5",
|
||||
"augslot6",
|
||||
"augment_one",
|
||||
"augment_two",
|
||||
"augment_three",
|
||||
"augment_four",
|
||||
"augment_five",
|
||||
"augment_six",
|
||||
"instnodrop",
|
||||
"custom_data",
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_icon",
|
||||
"ornament_idfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -123,22 +126,23 @@ public:
|
||||
{
|
||||
Inventory e{};
|
||||
|
||||
e.charid = 0;
|
||||
e.slotid = 0;
|
||||
e.itemid = 0;
|
||||
e.character_id = 0;
|
||||
e.slot_id = 0;
|
||||
e.item_id = 0;
|
||||
e.charges = 0;
|
||||
e.color = 0;
|
||||
e.augslot1 = 0;
|
||||
e.augslot2 = 0;
|
||||
e.augslot3 = 0;
|
||||
e.augslot4 = 0;
|
||||
e.augslot5 = 0;
|
||||
e.augslot6 = 0;
|
||||
e.augment_one = 0;
|
||||
e.augment_two = 0;
|
||||
e.augment_three = 0;
|
||||
e.augment_four = 0;
|
||||
e.augment_five = 0;
|
||||
e.augment_six = 0;
|
||||
e.instnodrop = 0;
|
||||
e.custom_data = "";
|
||||
e.ornamenticon = 0;
|
||||
e.ornamentidfile = 0;
|
||||
e.ornament_icon = 0;
|
||||
e.ornament_idfile = 0;
|
||||
e.ornament_hero_model = 0;
|
||||
e.guid = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -149,7 +153,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &inventory : inventorys) {
|
||||
if (inventory.charid == inventory_id) {
|
||||
if (inventory.character_id == inventory_id) {
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
@@ -175,22 +179,23 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
Inventory e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slotid = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.itemid = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.color = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.augslot1 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augslot2 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augslot3 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augslot4 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augslot5 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augslot6 = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
|
||||
e.augment_one = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augment_two = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augment_three = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augment_four = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augment_five = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augment_six = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
e.instnodrop = row[11] ? static_cast<uint8_t>(strtoul(row[11], nullptr, 10)) : 0;
|
||||
e.custom_data = row[12] ? row[12] : "";
|
||||
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornament_icon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornament_idfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -224,22 +229,23 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.charid));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.slotid));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.itemid));
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.character_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.slot_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.charges));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.color));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.augslot1));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.augslot2));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.augslot3));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.augslot4));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.augslot5));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.augslot6));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.augment_one));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.augment_two));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.augment_three));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.augment_four));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.augment_five));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.augment_six));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.instnodrop));
|
||||
v.push_back(columns[12] + " = '" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.ornament_icon));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.ornament_idfile));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model));
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -247,7 +253,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.charid
|
||||
e.character_id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -261,22 +267,23 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.slotid));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augslot1));
|
||||
v.push_back(std::to_string(e.augslot2));
|
||||
v.push_back(std::to_string(e.augslot3));
|
||||
v.push_back(std::to_string(e.augslot4));
|
||||
v.push_back(std::to_string(e.augslot5));
|
||||
v.push_back(std::to_string(e.augslot6));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back(std::to_string(e.instnodrop));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -287,7 +294,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.charid = results.LastInsertedID();
|
||||
e.character_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -306,22 +313,23 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.slotid));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augslot1));
|
||||
v.push_back(std::to_string(e.augslot2));
|
||||
v.push_back(std::to_string(e.augslot3));
|
||||
v.push_back(std::to_string(e.augslot4));
|
||||
v.push_back(std::to_string(e.augslot5));
|
||||
v.push_back(std::to_string(e.augslot6));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back(std::to_string(e.instnodrop));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -355,22 +363,23 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Inventory e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slotid = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.itemid = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.color = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.augslot1 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augslot2 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augslot3 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augslot4 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augslot5 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augslot6 = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
|
||||
e.augment_one = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augment_two = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augment_three = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augment_four = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augment_five = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augment_six = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
e.instnodrop = row[11] ? static_cast<uint8_t>(strtoul(row[11], nullptr, 10)) : 0;
|
||||
e.custom_data = row[12] ? row[12] : "";
|
||||
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornament_icon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornament_idfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -395,22 +404,23 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Inventory e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slotid = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.itemid = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.color = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.augslot1 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augslot2 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augslot3 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augslot4 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augslot5 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augslot6 = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
|
||||
e.augment_one = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augment_two = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augment_three = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augment_four = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augment_five = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augment_six = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
e.instnodrop = row[11] ? static_cast<uint8_t>(strtoul(row[11], nullptr, 10)) : 0;
|
||||
e.custom_data = row[12] ? row[12] : "";
|
||||
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornament_icon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornament_idfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -485,22 +495,23 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.slotid));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augslot1));
|
||||
v.push_back(std::to_string(e.augslot2));
|
||||
v.push_back(std::to_string(e.augslot3));
|
||||
v.push_back(std::to_string(e.augslot4));
|
||||
v.push_back(std::to_string(e.augslot5));
|
||||
v.push_back(std::to_string(e.augslot6));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back(std::to_string(e.instnodrop));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -523,22 +534,23 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.slotid));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augslot1));
|
||||
v.push_back(std::to_string(e.augslot2));
|
||||
v.push_back(std::to_string(e.augslot3));
|
||||
v.push_back(std::to_string(e.augslot4));
|
||||
v.push_back(std::to_string(e.augslot5));
|
||||
v.push_back(std::to_string(e.augslot6));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back(std::to_string(e.instnodrop));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
uint32_t ornamenticon;
|
||||
uint32_t ornamentidfile;
|
||||
int32_t ornament_hero_model;
|
||||
uint64_t guid;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -63,6 +64,7 @@ public:
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,6 +88,7 @@ public:
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,6 +146,7 @@ public:
|
||||
e.ornamenticon = 0;
|
||||
e.ornamentidfile = 0;
|
||||
e.ornament_hero_model = 0;
|
||||
e.guid = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -196,6 +200,7 @@ public:
|
||||
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -246,6 +251,7 @@ public:
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.ornamenticon));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.ornamentidfile));
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.ornament_hero_model));
|
||||
v.push_back(columns[17] + " = " + std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -284,6 +290,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -330,6 +337,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -380,6 +388,7 @@ public:
|
||||
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -421,6 +430,7 @@ public:
|
||||
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -512,6 +522,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -551,6 +562,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
v.push_back(std::to_string(e.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,560 @@
|
||||
/**
|
||||
* 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_SHAREDBANK_REPOSITORY_H
|
||||
#define EQEMU_BASE_SHAREDBANK_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseSharedbankRepository {
|
||||
public:
|
||||
struct Sharedbank {
|
||||
uint32_t account_id;
|
||||
uint32_t slot_id;
|
||||
uint32_t item_id;
|
||||
uint16_t charges;
|
||||
uint32_t color;
|
||||
uint32_t augment_one;
|
||||
uint32_t augment_two;
|
||||
uint32_t augment_three;
|
||||
uint32_t augment_four;
|
||||
uint32_t augment_five;
|
||||
uint32_t augment_six;
|
||||
std::string custom_data;
|
||||
uint32_t ornament_icon;
|
||||
uint32_t ornament_idfile;
|
||||
int32_t ornament_hero_model;
|
||||
uint64_t guid;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("account_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"account_id",
|
||||
"slot_id",
|
||||
"item_id",
|
||||
"charges",
|
||||
"color",
|
||||
"augment_one",
|
||||
"augment_two",
|
||||
"augment_three",
|
||||
"augment_four",
|
||||
"augment_five",
|
||||
"augment_six",
|
||||
"custom_data",
|
||||
"ornament_icon",
|
||||
"ornament_idfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"account_id",
|
||||
"slot_id",
|
||||
"item_id",
|
||||
"charges",
|
||||
"color",
|
||||
"augment_one",
|
||||
"augment_two",
|
||||
"augment_three",
|
||||
"augment_four",
|
||||
"augment_five",
|
||||
"augment_six",
|
||||
"custom_data",
|
||||
"ornament_icon",
|
||||
"ornament_idfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
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("sharedbank");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static Sharedbank NewEntity()
|
||||
{
|
||||
Sharedbank e{};
|
||||
|
||||
e.account_id = 0;
|
||||
e.slot_id = 0;
|
||||
e.item_id = 0;
|
||||
e.charges = 0;
|
||||
e.color = 0;
|
||||
e.augment_one = 0;
|
||||
e.augment_two = 0;
|
||||
e.augment_three = 0;
|
||||
e.augment_four = 0;
|
||||
e.augment_five = 0;
|
||||
e.augment_six = 0;
|
||||
e.custom_data = "";
|
||||
e.ornament_icon = 0;
|
||||
e.ornament_idfile = 0;
|
||||
e.ornament_hero_model = 0;
|
||||
e.guid = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static Sharedbank GetSharedbank(
|
||||
const std::vector<Sharedbank> &sharedbanks,
|
||||
int sharedbank_id
|
||||
)
|
||||
{
|
||||
for (auto &sharedbank : sharedbanks) {
|
||||
if (sharedbank.account_id == sharedbank_id) {
|
||||
return sharedbank;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static Sharedbank FindOne(
|
||||
Database& db,
|
||||
int sharedbank_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
sharedbank_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
Sharedbank e{};
|
||||
|
||||
e.account_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.color = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.augment_one = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augment_two = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augment_three = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augment_four = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augment_five = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augment_six = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
e.custom_data = row[11] ? row[11] : "";
|
||||
e.ornament_icon = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.ornament_idfile = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[14] ? static_cast<int32_t>(atoi(row[14])) : 0;
|
||||
e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int sharedbank_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
sharedbank_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const Sharedbank &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.account_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.slot_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.charges));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.color));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.augment_one));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.augment_two));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.augment_three));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.augment_four));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.augment_five));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.augment_six));
|
||||
v.push_back(columns[11] + " = '" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.ornament_icon));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.ornament_idfile));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.ornament_hero_model));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.account_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static Sharedbank InsertOne(
|
||||
Database& db,
|
||||
Sharedbank e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.account_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<Sharedbank> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
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<Sharedbank> All(Database& db)
|
||||
{
|
||||
std::vector<Sharedbank> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Sharedbank e{};
|
||||
|
||||
e.account_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.color = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.augment_one = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augment_two = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augment_three = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augment_four = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augment_five = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augment_six = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
e.custom_data = row[11] ? row[11] : "";
|
||||
e.ornament_icon = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.ornament_idfile = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[14] ? static_cast<int32_t>(atoi(row[14])) : 0;
|
||||
e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<Sharedbank> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<Sharedbank> 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) {
|
||||
Sharedbank e{};
|
||||
|
||||
e.account_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.slot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.color = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.augment_one = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.augment_two = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.augment_three = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.augment_four = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.augment_five = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.augment_six = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
e.custom_data = row[11] ? row[11] : "";
|
||||
e.ornament_icon = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.ornament_idfile = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.ornament_hero_model = row[14] ? static_cast<int32_t>(atoi(row[14])) : 0;
|
||||
e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0;
|
||||
|
||||
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 Sharedbank &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
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<Sharedbank> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.color));
|
||||
v.push_back(std::to_string(e.augment_one));
|
||||
v.push_back(std::to_string(e.augment_two));
|
||||
v.push_back(std::to_string(e.augment_three));
|
||||
v.push_back(std::to_string(e.augment_four));
|
||||
v.push_back(std::to_string(e.augment_five));
|
||||
v.push_back(std::to_string(e.augment_six));
|
||||
v.push_back("'" + Strings::Escape(e.custom_data) + "'");
|
||||
v.push_back(std::to_string(e.ornament_icon));
|
||||
v.push_back(std::to_string(e.ornament_idfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
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_SHAREDBANK_REPOSITORY_H
|
||||
@@ -19,31 +19,34 @@
|
||||
class BaseSpellBucketsRepository {
|
||||
public:
|
||||
struct SpellBuckets {
|
||||
uint64_t spellid;
|
||||
std::string key_;
|
||||
std::string value;
|
||||
uint32_t spell_id;
|
||||
std::string bucket_name;
|
||||
std::string bucket_value;
|
||||
uint8_t bucket_comparison;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("spellid");
|
||||
return std::string("spell_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"spellid",
|
||||
"`key`",
|
||||
"value",
|
||||
"spell_id",
|
||||
"bucket_name",
|
||||
"bucket_value",
|
||||
"bucket_comparison",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"spellid",
|
||||
"`key`",
|
||||
"value",
|
||||
"spell_id",
|
||||
"bucket_name",
|
||||
"bucket_value",
|
||||
"bucket_comparison",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,9 +87,10 @@ public:
|
||||
{
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = 0;
|
||||
e.key_ = "";
|
||||
e.value = "";
|
||||
e.spell_id = 0;
|
||||
e.bucket_name = "";
|
||||
e.bucket_value = "";
|
||||
e.bucket_comparison = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -97,7 +101,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &spell_buckets : spell_bucketss) {
|
||||
if (spell_buckets.spellid == spell_buckets_id) {
|
||||
if (spell_buckets.spell_id == spell_buckets_id) {
|
||||
return spell_buckets;
|
||||
}
|
||||
}
|
||||
@@ -123,9 +127,10 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.key_ = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bucket_name = row[1] ? row[1] : "";
|
||||
e.bucket_value = row[2] ? row[2] : "";
|
||||
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -159,9 +164,10 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.spellid));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.spell_id));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.bucket_comparison));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -169,7 +175,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.spellid
|
||||
e.spell_id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -183,9 +189,10 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -196,7 +203,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.spellid = results.LastInsertedID();
|
||||
e.spell_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -215,9 +222,10 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -251,9 +259,10 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.key_ = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bucket_name = row[1] ? row[1] : "";
|
||||
e.bucket_value = row[2] ? row[2] : "";
|
||||
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -278,9 +287,10 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.key_ = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bucket_name = row[1] ? row[1] : "";
|
||||
e.bucket_value = row[2] ? row[2] : "";
|
||||
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -355,9 +365,10 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -380,9 +391,10 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -44,7 +44,24 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static std::vector<std::string> GetBaseDataFileLines(Database& db)
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT CONCAT_WS('^', {}) FROM {} ORDER BY `level`, `class` ASC",
|
||||
ColumnsRaw(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
for (auto row : results) {
|
||||
lines.emplace_back(row[0]);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_DATA_REPOSITORY_H
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
#ifndef EQEMU_BUYER_BUY_LINES_REPOSITORY_H
|
||||
#define EQEMU_BUYER_BUY_LINES_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_buyer_buy_lines_repository.h"
|
||||
#include "buyer_trade_items_repository.h"
|
||||
#include "character_data_repository.h"
|
||||
#include "buyer_repository.h"
|
||||
|
||||
#include "../eq_packet_structs.h"
|
||||
|
||||
class BuyerBuyLinesRepository: public BaseBuyerBuyLinesRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* BuyerBuyLinesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* BuyerBuyLinesRepository::GetWhereNeverExpires()
|
||||
* BuyerBuyLinesRepository::GetWhereXAndY()
|
||||
* BuyerBuyLinesRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
struct WelcomeData_Struct {
|
||||
uint32 count_of_buyers;
|
||||
uint32 count_of_items;
|
||||
};
|
||||
|
||||
static int CreateBuyLine(Database& db, const BuyerLineItems_Struct b, uint32 char_id)
|
||||
{
|
||||
auto buyer = BuyerRepository::GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1", char_id));
|
||||
if (buyer.empty()){
|
||||
return 0;
|
||||
}
|
||||
|
||||
BuyerBuyLinesRepository::BuyerBuyLines buy_lines{};
|
||||
buy_lines.id = 0;
|
||||
buy_lines.buyer_id = buyer.front().id;
|
||||
buy_lines.char_id = char_id;
|
||||
buy_lines.buy_slot_id = b.slot;
|
||||
buy_lines.item_id = b.item_id;
|
||||
buy_lines.item_name = b.item_name;
|
||||
buy_lines.item_icon = b.item_icon;
|
||||
buy_lines.item_qty = b.item_quantity;
|
||||
buy_lines.item_price = b.item_cost;
|
||||
auto e = InsertOne(db, buy_lines);
|
||||
|
||||
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> queue;
|
||||
|
||||
for (auto const &r: b.trade_items) {
|
||||
BuyerTradeItemsRepository::BuyerTradeItems bti{};
|
||||
bti.id = 0;
|
||||
bti.buyer_buy_lines_id = e.id;
|
||||
bti.item_id = r.item_id;
|
||||
bti.item_qty = r.item_quantity;
|
||||
bti.item_icon = r.item_icon;
|
||||
bti.item_name = r.item_name;
|
||||
|
||||
if (bti.item_id) {
|
||||
queue.push_back(bti);
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue.empty()) {
|
||||
BuyerTradeItemsRepository::InsertMany(db, queue);
|
||||
}
|
||||
|
||||
return e.id;
|
||||
}
|
||||
|
||||
static int ModifyBuyLine(Database& db, const BuyerLineItems_Struct b, uint32 char_id)
|
||||
{
|
||||
auto b_lines = GetWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}';", char_id, b.slot));
|
||||
if (b_lines.empty() || b_lines.size() > 1){
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto b_line = b_lines.front();
|
||||
|
||||
b_line.item_qty = b.item_quantity;
|
||||
b_line.item_price = b.item_cost;
|
||||
b_line.item_icon = b.item_icon;
|
||||
auto e = UpdateOne(db, b_line);
|
||||
|
||||
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> queue;
|
||||
|
||||
BuyerTradeItemsRepository::DeleteWhere(db, fmt::format("`buyer_buy_lines_id` = '{}';", b_line.id));
|
||||
for (auto const &r: b.trade_items) {
|
||||
BuyerTradeItemsRepository::BuyerTradeItems bti{};
|
||||
bti.id = 0;
|
||||
bti.buyer_buy_lines_id = b_line.id;
|
||||
bti.item_id = r.item_id;
|
||||
bti.item_qty = r.item_quantity;
|
||||
bti.item_icon = r.item_icon;
|
||||
bti.item_name = r.item_name;
|
||||
|
||||
if (bti.item_id) {
|
||||
queue.push_back(bti);
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue.empty()) {
|
||||
BuyerTradeItemsRepository::InsertMany(db, queue);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool DeleteBuyLine(Database &db, uint32 char_id, int32 slot_id = 0xffffffff)
|
||||
{
|
||||
std::vector<BuyerBuyLines> buylines{};
|
||||
if (slot_id == 0xffffffff) {
|
||||
auto buylines = GetWhere(db, fmt::format("`char_id` = '{}'", char_id));
|
||||
DeleteWhere(db, fmt::format("`char_id` = '{}'", char_id));
|
||||
}
|
||||
else {
|
||||
auto buylines = GetWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}'", char_id, slot_id));
|
||||
DeleteWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}'", char_id, slot_id));
|
||||
}
|
||||
|
||||
if (buylines.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> buyline_ids{};
|
||||
for (auto const &bl: buylines) {
|
||||
buyline_ids.push_back((std::to_string(bl.id)));
|
||||
}
|
||||
|
||||
if (!buyline_ids.empty()) {
|
||||
BuyerTradeItemsRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`buyer_buy_lines_id` IN({})",
|
||||
Strings::Implode(", ", buyline_ids)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::vector<BuyerLineItems_Struct> GetBuyLines(Database &db, uint32 char_id)
|
||||
{
|
||||
std::vector<BuyerLineItems_Struct> all_entries{};
|
||||
|
||||
auto buy_line = GetWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||
if (buy_line.empty()){
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
auto buy_line_trade_items = BuyerTradeItemsRepository::GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`buyer_buy_lines_id` IN (SELECT b.id FROM buyer_buy_lines AS b WHERE b.char_id = '{}')",
|
||||
char_id
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(buy_line.size());
|
||||
|
||||
for (auto const &l: buy_line) {
|
||||
BuyerLineItems_Struct bli{};
|
||||
bli.item_id = l.item_id;
|
||||
bli.item_cost = l.item_price;
|
||||
bli.item_quantity = l.item_qty;
|
||||
bli.slot = l.buy_slot_id;
|
||||
bli.item_name = l.item_name;
|
||||
|
||||
for (auto const &i: GetSubIDs(buy_line_trade_items, l.id)) {
|
||||
BuyerLineTradeItems_Struct blti{};
|
||||
blti.item_id = i.item_id;
|
||||
blti.item_icon = i.item_icon;
|
||||
blti.item_quantity = i.item_qty;
|
||||
blti.item_id = i.item_id;
|
||||
blti.item_name = i.item_name;
|
||||
bli.trade_items.push_back(blti);
|
||||
}
|
||||
all_entries.push_back(bli);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static BuyerLineSearch_Struct SearchBuyLines(
|
||||
Database &db,
|
||||
std::string &search_string,
|
||||
uint32 char_id = 0,
|
||||
uint32 zone_id = 0,
|
||||
uint32 zone_instance_id = 0
|
||||
)
|
||||
{
|
||||
BuyerLineSearch_Struct all_entries{};
|
||||
std::string where_clause(fmt::format("`item_name` LIKE \"%{}%\" ", search_string));
|
||||
|
||||
if (char_id) {
|
||||
where_clause += fmt::format("AND `char_id` = '{}' ", char_id);
|
||||
}
|
||||
|
||||
if (zone_id) {
|
||||
auto buyers = BuyerRepository::GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`char_zone_id` = '{}' AND char_zone_instance_id = '{}'",
|
||||
zone_id,
|
||||
zone_instance_id
|
||||
)
|
||||
);
|
||||
|
||||
if (buyers.empty()) {
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
std::vector<std::string> char_ids{};
|
||||
for (auto const &bl : buyers) {
|
||||
char_ids.push_back((std::to_string(bl.char_id)));
|
||||
}
|
||||
|
||||
where_clause += fmt::format("AND `char_id` IN ({}) ", Strings::Implode(", ", char_ids));
|
||||
}
|
||||
|
||||
auto buy_line = GetWhere(db, where_clause);
|
||||
if (buy_line.empty()){
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
std::vector<std::string> ids{};
|
||||
std::vector<std::string> char_ids{};
|
||||
for (auto const &bl : buy_line) {
|
||||
if (std::find(ids.begin(), ids.end(), std::to_string(bl.id)) == std::end(ids)) {
|
||||
ids.push_back(std::to_string(bl.id));
|
||||
}
|
||||
if (std::find(char_ids.begin(), char_ids.end(), std::to_string(bl.char_id)) == std::end(char_ids)) {
|
||||
char_ids.push_back((std::to_string(bl.char_id)));
|
||||
}
|
||||
}
|
||||
|
||||
auto buy_line_trade_items = BuyerTradeItemsRepository::GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`buyer_buy_lines_id` IN ({});",
|
||||
Strings::Implode(", ", ids)
|
||||
)
|
||||
);
|
||||
|
||||
auto char_names = BuyerRepository::GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`char_id` IN ({});",
|
||||
Strings::Implode(", ", char_ids)
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.no_items = buy_line.size();
|
||||
for (auto const &l: buy_line) {
|
||||
BuyerLineItemsSearch_Struct blis{};
|
||||
blis.slot = l.buy_slot_id;
|
||||
blis.item_id = l.item_id;
|
||||
blis.item_cost = l.item_price;
|
||||
blis.item_icon = l.item_icon;
|
||||
blis.item_quantity = l.item_qty;
|
||||
blis.buyer_id = l.char_id;
|
||||
auto it = std::find_if(
|
||||
char_names.cbegin(),
|
||||
char_names.cend(),
|
||||
[&](BuyerRepository::Buyer e) { return e.char_id == l.char_id; }
|
||||
);
|
||||
blis.buyer_name = it != char_names.end() ? it->char_name : std::string("");
|
||||
blis.buyer_entity_id = it != char_names.end() ? it->char_entity_id : 0;
|
||||
blis.buyer_zone_id = it != char_names.end() ? it->char_zone_id : 0;
|
||||
blis.buyer_zone_instance_id = it != char_names.end() ? it->char_zone_instance_id : 0;
|
||||
strn0cpy(blis.item_name, l.item_name.c_str(), sizeof(blis.item_name));
|
||||
|
||||
for (auto const &i: GetSubIDs(buy_line_trade_items, l.id)) {
|
||||
BuyerLineTradeItems_Struct e{};
|
||||
e.item_id = i.item_id;
|
||||
e.item_icon = i.item_icon;
|
||||
e.item_quantity = i.item_qty;
|
||||
e.item_id = i.item_id;
|
||||
e.item_name = i.item_name;
|
||||
|
||||
blis.trade_items.push_back(e);
|
||||
}
|
||||
all_entries.buy_line.push_back(blis);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BuyerTradeItemsRepository::BuyerTradeItems>
|
||||
GetSubIDs(std::vector<BuyerTradeItemsRepository::BuyerTradeItems> &in, uint64 id)
|
||||
{
|
||||
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> results{};
|
||||
std::vector<uint64> indices{};
|
||||
|
||||
auto it = in.begin();
|
||||
while ((it = std::find_if(
|
||||
it,
|
||||
in.end(),
|
||||
[&](BuyerTradeItemsRepository::BuyerTradeItems const &e) {
|
||||
return e.buyer_buy_lines_id == id;
|
||||
}
|
||||
))
|
||||
!= in.end()
|
||||
) {
|
||||
indices.push_back(std::distance(in.begin(), it));
|
||||
results.push_back(*it);
|
||||
it++;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
static WelcomeData_Struct GetWelcomeData(Database &db)
|
||||
{
|
||||
WelcomeData_Struct e{};
|
||||
|
||||
auto results = db.QueryDatabase("SELECT COUNT(DISTINCT char_id), COUNT(char_id) FROM buyer;");
|
||||
|
||||
if (!results.RowCount()) {
|
||||
return e;
|
||||
}
|
||||
|
||||
auto r = results.begin();
|
||||
e.count_of_buyers = Strings::ToInt(r[0]);
|
||||
e.count_of_items = Strings::ToInt(r[1]);
|
||||
return e;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BUYER_BUY_LINES_REPOSITORY_H
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_buyer_repository.h"
|
||||
#include "base/base_buyer_trade_items_repository.h"
|
||||
#include "base/base_buyer_buy_lines_repository.h"
|
||||
|
||||
class BuyerRepository: public BaseBuyerRepository {
|
||||
public:
|
||||
@@ -45,6 +47,97 @@ public:
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
static bool UpdateWelcomeMessage(Database& db, uint32 char_id, const char *message) {
|
||||
|
||||
auto const b = GetWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||
|
||||
if (b.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto buyer = b.front();
|
||||
buyer.welcome_message = message;
|
||||
return UpdateOne(db, buyer);
|
||||
}
|
||||
|
||||
static std::string GetWelcomeMessage(Database& db, uint32 char_id) {
|
||||
|
||||
auto const b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||
if (b.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return b.front().welcome_message;
|
||||
}
|
||||
|
||||
static int UpdateTransactionDate(Database& db, uint32 char_id, time_t transaction_date) {
|
||||
auto b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||
if (b.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto e = b.front();
|
||||
e.transaction_date = transaction_date;
|
||||
|
||||
return UpdateOne(db, e);
|
||||
}
|
||||
|
||||
static time_t GetTransactionDate(Database& db, uint32 char_id) {
|
||||
auto b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||
if (b.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto e = b.front();
|
||||
|
||||
return e.transaction_date;
|
||||
}
|
||||
|
||||
static bool DeleteBuyer(Database &db, uint32 char_id)
|
||||
{
|
||||
if (char_id == 0) {
|
||||
Truncate(db);
|
||||
BaseBuyerBuyLinesRepository::Truncate(db);
|
||||
BaseBuyerTradeItemsRepository::Truncate(db);
|
||||
}
|
||||
else {
|
||||
auto buyer = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||
if (buyer.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
|
||||
db,
|
||||
fmt::format("`buyer_id` = '{}'", buyer.front().id)
|
||||
);
|
||||
if (buy_lines.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> buy_line_ids{};
|
||||
for (auto const &bl: buy_lines) {
|
||||
buy_line_ids.push_back(std::to_string(bl.id));
|
||||
}
|
||||
|
||||
DeleteWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||
if (buy_line_ids.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BaseBuyerBuyLinesRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||
);
|
||||
BaseBuyerTradeItemsRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`buyer_buy_lines_id` IN({})",
|
||||
Strings::Implode(", ", buy_line_ids))
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BUYER_REPOSITORY_H
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||
#define EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_buyer_trade_items_repository.h"
|
||||
|
||||
class BuyerTradeItemsRepository: public BaseBuyerTradeItemsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* BuyerTradeItemsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* BuyerTradeItemsRepository::GetWhereNeverExpires()
|
||||
* BuyerTradeItemsRepository::GetWhereXAndY()
|
||||
* BuyerTradeItemsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
static std::vector<BuyerTradeItems> GetTradeItems(Database& db, const uint32 char_id)
|
||||
{
|
||||
std::vector<BuyerTradeItems> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT bti.* "
|
||||
"FROM buyer_trade_items AS bti "
|
||||
"INNER JOIN buyer_buy_lines AS bbl ON bti.buyer_buy_lines_id = bbl.id "
|
||||
"WHERE bbl.char_id = '{}';",
|
||||
char_id
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BuyerTradeItems e{};
|
||||
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.item_name = row[5] ? row[5] : "0";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
// these are the base definitions for command_subsettings and can be over-ridden by the database
|
||||
std::vector<CommandSubsettingsRepository::CommandSubsettings> static_records = {
|
||||
{.parent_command = "find", .sub_command = "aa", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findaa"},
|
||||
{.parent_command = "find", .sub_command = "body_type", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findbodytype"},
|
||||
{.parent_command = "find", .sub_command = "bug_category", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findbugcategory"},
|
||||
{.parent_command = "find", .sub_command = "character", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findcharacter"},
|
||||
{.parent_command = "find", .sub_command = "class", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findclass"},
|
||||
@@ -60,10 +61,12 @@ public:
|
||||
{.parent_command = "find", .sub_command = "item", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fi|finditem|itemsearch"},
|
||||
{.parent_command = "find", .sub_command = "language", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findlanguage"},
|
||||
{.parent_command = "find", .sub_command = "npc_type", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fn|findnpc|findnpctype"},
|
||||
{.parent_command = "find", .sub_command = "object_type", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findobjecttype"},
|
||||
{.parent_command = "find", .sub_command = "race", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findrace"},
|
||||
{.parent_command = "find", .sub_command = "recipe", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findrecipe"},
|
||||
{.parent_command = "find", .sub_command = "skill", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findskill"},
|
||||
{.parent_command = "find", .sub_command = "special_ability", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fsa|findspecialability"},
|
||||
{.parent_command = "find", .sub_command = "stance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findstance"},
|
||||
{.parent_command = "find", .sub_command = "spell", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fs|findspell"},
|
||||
{.parent_command = "find", .sub_command = "task", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findtask"},
|
||||
{.parent_command = "find", .sub_command = "zone", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fz|findzone"},
|
||||
@@ -82,7 +85,6 @@ public:
|
||||
{.parent_command = "set", .sub_command = "endurance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setendurance"},
|
||||
{.parent_command = "set", .sub_command = "endurance_full", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "endurance"},
|
||||
{.parent_command = "set", .sub_command = "exp", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setxp|setexp"},
|
||||
{.parent_command = "set", .sub_command = "faction", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setfaction"},
|
||||
{.parent_command = "set", .sub_command = "flymode", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "flymode"},
|
||||
{.parent_command = "set", .sub_command = "freeze", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "freeze|unfreeze"},
|
||||
{.parent_command = "set", .sub_command = "gender", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "gender"},
|
||||
|
||||
@@ -44,7 +44,24 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static std::vector<std::string> GetDBStrFileLines(Database& db)
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT CONCAT(CONCAT_WS('^', {}), '^0') FROM {} ORDER BY `id`, `type` ASC",
|
||||
ColumnsRaw(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
for (auto row : results) {
|
||||
lines.emplace_back(row[0]);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_DB_STR_REPOSITORY_H
|
||||
|
||||
@@ -3,310 +3,47 @@
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_sharedbank_repository.h"
|
||||
|
||||
class SharedbankRepository {
|
||||
class SharedbankRepository: public BaseSharedbankRepository {
|
||||
public:
|
||||
struct Sharedbank {
|
||||
int acctid;
|
||||
int slotid;
|
||||
int itemid;
|
||||
int16 charges;
|
||||
int augslot1;
|
||||
int augslot2;
|
||||
int augslot3;
|
||||
int augslot4;
|
||||
int augslot5;
|
||||
int augslot6;
|
||||
std::string custom_data;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* SharedbankRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* SharedbankRepository::GetWhereNeverExpires()
|
||||
* SharedbankRepository::GetWhereXAndY()
|
||||
* SharedbankRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"acctid",
|
||||
"slotid",
|
||||
"itemid",
|
||||
"charges",
|
||||
"augslot1",
|
||||
"augslot2",
|
||||
"augslot3",
|
||||
"augslot4",
|
||||
"augslot5",
|
||||
"augslot6",
|
||||
"custom_data",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string InsertColumnsRaw()
|
||||
{
|
||||
std::vector<std::string> insert_columns;
|
||||
|
||||
for (auto &column : Columns()) {
|
||||
if (column == PrimaryKey()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
insert_columns.push_back(column);
|
||||
}
|
||||
|
||||
return std::string(Strings::Implode(", ", insert_columns));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("sharedbank");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
ColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
InsertColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static Sharedbank NewEntity()
|
||||
{
|
||||
Sharedbank entry{};
|
||||
|
||||
entry.acctid = 0;
|
||||
entry.slotid = 0;
|
||||
entry.itemid = 0;
|
||||
entry.charges = 0;
|
||||
entry.augslot1 = 0;
|
||||
entry.augslot2 = 0;
|
||||
entry.augslot3 = 0;
|
||||
entry.augslot4 = 0;
|
||||
entry.augslot5 = 0;
|
||||
entry.augslot6 = 0;
|
||||
entry.custom_data = 0;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static Sharedbank GetSharedbankEntry(
|
||||
const std::vector<Sharedbank> &sharedbanks,
|
||||
int sharedbank_id
|
||||
)
|
||||
{
|
||||
for (auto &sharedbank : sharedbanks) {
|
||||
if (sharedbank. == sharedbank_id) {
|
||||
return sharedbank;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static Sharedbank FindOne(
|
||||
int sharedbank_id
|
||||
)
|
||||
{
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
sharedbank_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
Sharedbank entry{};
|
||||
|
||||
entry.acctid = atoi(row[0]);
|
||||
entry.slotid = atoi(row[1]);
|
||||
entry.itemid = atoi(row[2]);
|
||||
entry.charges = atoi(row[3]);
|
||||
entry.augslot1 = atoi(row[4]);
|
||||
entry.augslot2 = atoi(row[5]);
|
||||
entry.augslot3 = atoi(row[6]);
|
||||
entry.augslot4 = atoi(row[7]);
|
||||
entry.augslot5 = atoi(row[8]);
|
||||
entry.augslot6 = atoi(row[9]);
|
||||
entry.custom_data = row[10];
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
int sharedbank_id
|
||||
)
|
||||
{
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
sharedbank_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Sharedbank sharedbank_entry
|
||||
)
|
||||
{
|
||||
std::vector<std::string> update_values;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
update_values.push_back(columns[0] + " = " + std::to_string(sharedbank_entry.acctid));
|
||||
update_values.push_back(columns[1] + " = " + std::to_string(sharedbank_entry.slotid));
|
||||
update_values.push_back(columns[2] + " = " + std::to_string(sharedbank_entry.itemid));
|
||||
update_values.push_back(columns[3] + " = " + std::to_string(sharedbank_entry.charges));
|
||||
update_values.push_back(columns[4] + " = " + std::to_string(sharedbank_entry.augslot1));
|
||||
update_values.push_back(columns[5] + " = " + std::to_string(sharedbank_entry.augslot2));
|
||||
update_values.push_back(columns[6] + " = " + std::to_string(sharedbank_entry.augslot3));
|
||||
update_values.push_back(columns[7] + " = " + std::to_string(sharedbank_entry.augslot4));
|
||||
update_values.push_back(columns[8] + " = " + std::to_string(sharedbank_entry.augslot5));
|
||||
update_values.push_back(columns[9] + " = " + std::to_string(sharedbank_entry.augslot6));
|
||||
update_values.push_back(columns[10] + " = '" + Strings::Escape(sharedbank_entry.custom_data) + "'");
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", update_values),
|
||||
PrimaryKey(),
|
||||
sharedbank_entry.
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static Sharedbank InsertOne(
|
||||
Sharedbank sharedbank_entry
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.acctid));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.slotid));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.itemid));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.charges));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot1));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot2));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot3));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot4));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot5));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot6));
|
||||
insert_values.push_back("'" + Strings::Escape(sharedbank_entry.custom_data) + "'");
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_values)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
sharedbank_entry.id = results.LastInsertedID();
|
||||
return sharedbank_entry;
|
||||
}
|
||||
|
||||
sharedbank_entry = InstanceListRepository::NewEntity();
|
||||
|
||||
return sharedbank_entry;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
std::vector<Sharedbank> sharedbank_entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &sharedbank_entry: sharedbank_entries) {
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.acctid));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.slotid));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.itemid));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.charges));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot1));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot2));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot3));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot4));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot5));
|
||||
insert_values.push_back(std::to_string(sharedbank_entry.augslot6));
|
||||
insert_values.push_back("'" + Strings::Escape(sharedbank_entry.custom_data) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<Sharedbank> All()
|
||||
{
|
||||
std::vector<Sharedbank> all_entries;
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Sharedbank entry{};
|
||||
|
||||
entry.acctid = atoi(row[0]);
|
||||
entry.slotid = atoi(row[1]);
|
||||
entry.itemid = atoi(row[2]);
|
||||
entry.charges = atoi(row[3]);
|
||||
entry.augslot1 = atoi(row[4]);
|
||||
entry.augslot2 = atoi(row[5]);
|
||||
entry.augslot3 = atoi(row[6]);
|
||||
entry.augslot4 = atoi(row[7]);
|
||||
entry.augslot5 = atoi(row[8]);
|
||||
entry.augslot6 = atoi(row[9]);
|
||||
entry.custom_data = row[10];
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,23 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static std::vector<std::string> GetSkillCapFileLines(Database& db)
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT CONCAT_WS('^', `class_id`, `skill_id`, `level`, `cap`, `class_`) FROM {} ORDER BY `class_id`, `skill_id`, `level` ASC",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
for (auto row : results) {
|
||||
lines.emplace_back(row[0]);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_SKILL_CAPS_REPOSITORY_H
|
||||
|
||||
@@ -44,7 +44,25 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static std::vector<std::string> GetSpellFileLines(Database& db)
|
||||
{
|
||||
std::vector<std::string> lines;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT CONCAT_WS('^', {}) FROM {} ORDER BY {} ASC",
|
||||
ColumnsRaw(),
|
||||
TableName(),
|
||||
PrimaryKey()
|
||||
)
|
||||
);
|
||||
|
||||
for (auto row : results) {
|
||||
lines.emplace_back(row[0]);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_SPELLS_NEW_REPOSITORY_H
|
||||
|
||||
@@ -164,37 +164,35 @@ public:
|
||||
return UpdateOne(db, m);
|
||||
}
|
||||
|
||||
static Trader GetItemBySerialNumber(Database &db, uint32 serial_number)
|
||||
static Trader GetItemBySerialNumber(Database &db, uint32 serial_number, uint32 trader_id)
|
||||
{
|
||||
Trader e{};
|
||||
const auto trader_item = GetWhere(
|
||||
db,
|
||||
fmt::format("`item_sn` = '{}' LIMIT 1", serial_number)
|
||||
fmt::format("`char_id` = '{}' AND `item_sn` = '{}' LIMIT 1", trader_id, serial_number)
|
||||
);
|
||||
|
||||
if (trader_item.empty()) {
|
||||
return e;
|
||||
}
|
||||
else {
|
||||
return trader_item.at(0);
|
||||
}
|
||||
|
||||
return trader_item.at(0);
|
||||
}
|
||||
|
||||
static Trader GetItemBySerialNumber(Database &db, std::string serial_number)
|
||||
static Trader GetItemBySerialNumber(Database &db, std::string serial_number, uint32 trader_id)
|
||||
{
|
||||
Trader e{};
|
||||
auto sn = Strings::ToUnsignedBigInt(serial_number);
|
||||
const auto trader_item = GetWhere(
|
||||
db,
|
||||
fmt::format("`item_sn` = '{}' LIMIT 1", sn)
|
||||
fmt::format("`char_id` = '{}' AND `item_sn` = '{}' LIMIT 1", trader_id, sn)
|
||||
);
|
||||
|
||||
if (trader_item.empty()) {
|
||||
return e;
|
||||
}
|
||||
else {
|
||||
return trader_item.at(0);
|
||||
}
|
||||
|
||||
return trader_item.at(0);
|
||||
}
|
||||
|
||||
static int UpdateActiveTransaction(Database &db, uint32 id, bool status)
|
||||
@@ -217,6 +215,10 @@ public:
|
||||
delete_ids.push_back(std::to_string(e.id));
|
||||
}
|
||||
|
||||
if (delete_ids.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -189,6 +189,8 @@ void RuleManager::ResetRules(bool reload) {
|
||||
m_RuleRealValues[ Real__##rule_name ] = default_value;
|
||||
#define RULE_BOOL(category_name, rule_name, default_value, notes) \
|
||||
m_RuleBoolValues[ Bool__##rule_name ] = default_value;
|
||||
#define RULE_STRING(category_name, rule_name, default_value, notes) \
|
||||
m_RuleStringValues[ String__##rule_name ] = default_value;
|
||||
#include "ruletypes.h"
|
||||
|
||||
// restore these rules to their pre-reset values
|
||||
|
||||
+30
-2
@@ -178,6 +178,7 @@ RULE_BOOL(Character, NoSkillsOnHorse, false, "Enabling this will prevent Bind Wo
|
||||
RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing")
|
||||
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
|
||||
RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
|
||||
RULE_INT(Character, DefaultGuildRank, 8, "Default guild rank when Character:DefaultGuild is a non-0 value, default is 8 (Recruit)")
|
||||
RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared")
|
||||
RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.")
|
||||
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
|
||||
@@ -226,6 +227,9 @@ RULE_INT(Character, ClearXTargetDelay, 10, "Seconds between uses of the #clearxt
|
||||
RULE_BOOL(Character, PreventMountsFromZoning, false, "Enable to prevent mounts from zoning - Prior to December 15, 2004 this is enabled.")
|
||||
RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require players to have invitee on target (Disables /invite name) - Classic Style")
|
||||
RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a player to player trade, if lore items exist, it will output which items.")
|
||||
RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199")
|
||||
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
||||
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@@ -240,6 +244,9 @@ RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a mer
|
||||
RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse")
|
||||
RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor")
|
||||
RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat")
|
||||
RULE_BOOL(Mercs, MercsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||
RULE_INT(Mercs, MercsHasteCap, 100, "Haste cap for non-v3(over haste) haste")
|
||||
RULE_INT(Mercs, MercsHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Guild)
|
||||
@@ -331,6 +338,8 @@ RULE_STRING(World, IPExemptionZones, "", "Comma-delimited list of zones to exclu
|
||||
RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to have this be used instead of variables table 'motd' value")
|
||||
RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C")
|
||||
RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame")
|
||||
RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag")
|
||||
RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Zone)
|
||||
@@ -468,7 +477,6 @@ RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximu
|
||||
RULE_REAL(Spells, CallOfTheHeroAggroClearDist, 250.0, "Distance at which CoTH will wipe aggro. To disable and always enable aggro wipe on any distance of CoTH, set to 0.")
|
||||
RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs")
|
||||
RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002")
|
||||
RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.")
|
||||
RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.")
|
||||
RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.")
|
||||
RULE_BOOL(Spells, AllowDoubleInvis, true, "Allows you to cast invisibility spells on a player that is already invisible, live like behavior.")
|
||||
@@ -503,9 +511,15 @@ RULE_BOOL(Spells, RequireMnemonicRetention, true, "Enabling will require spell s
|
||||
RULE_BOOL(Spells, EvacClearCharmPet, false, "Enable to have evac in zone clear charm from charm pets and detach buffs.")
|
||||
RULE_BOOL(Spells, ManaTapsRequireNPCMana, false, "Enabling will require target to have mana to tap. Default off as many npc's are caster class with 0 mana and need fixed.")
|
||||
RULE_INT(Spells, HarmTouchCritRatio, 200, "Harmtouch crit bonus, on top of BaseCritRatio")
|
||||
RULE_BOOL(Spells, UseClassicHarmTouchDamage, false, "Use pre 2007 Harm Touch calculations - Default: False")
|
||||
RULE_BOOL(Spells, UseClassicSpellFocus, false, "Enabling will tell the server to handle random focus damage as classic spell imports lack the limit values.")
|
||||
RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.")
|
||||
RULE_INT(Spells, HealAmountMessageFilterThreshold, 100, "Lifetaps below this threshold will not have a message sent to the client (Heal will still process) 0 to Disable.")
|
||||
RULE_BOOL(Spells, SnareOverridesSpeedBonuses, false, "Enabling will allow snares to override any speed bonuses the entity may have. Default: False")
|
||||
RULE_INT(Spells, TargetedAOEMaxTargets, 4, "Max number of targets a Targeted 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_BOOL(Spells, AllowFocusOnSkillDamageSpells, false, "Allow focus effects 185, 459, and 482 to enhance SkillAttack spell effect 193")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@@ -615,7 +629,8 @@ RULE_INT(Combat, PCAccuracyAvoidanceMod2Scale, 100, "Scale Factor for PC Accurac
|
||||
RULE_BOOL(Combat, AllowRaidTargetBlind, false, "Toggle to allow raid targets to be blinded, default is false (Live-like)")
|
||||
RULE_BOOL(Combat, RogueBackstabHasteCorrection, false, "Toggle to enable correction for Haste impacting Backstab DPS too much. DEFAULT: false")
|
||||
RULE_BOOL(Combat, LegacyComputeDefense, false, "Trim AGI Scaling of defense mostly for lower levels to help compensate for the newer agi based defense system. Default: False")
|
||||
RULE_REAL(Combat, SlayDamageAdjustment, 0.5, "Slay Damage Adjustment - Multiply final slay damage by this value. Default: 0.5")
|
||||
RULE_REAL(Combat, SlayDamageMultiplier, 1.0, "Slay Damage Adjustment - Multiply final slay damage by this value. Default: 1.0")
|
||||
RULE_REAL(Combat, SlayRateMultiplier, 1.0, "Slay Rate Adjustments - Multiply final slay rate check by this value. Default: 1.0")
|
||||
RULE_INT(Combat, MaximumLevelStunsCripplingBlow, 55, "Maximum level that Crippling Blows will stun a npc. Default: 55")
|
||||
RULE_INT(Combat, ArcheryBaseDamage, 0, "Archery base damage, default is 0")
|
||||
RULE_INT(Combat, BackstabBaseDamage, 0, "Backstab base damage, default is 0")
|
||||
@@ -663,6 +678,9 @@ RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if e
|
||||
RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them")
|
||||
RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732")
|
||||
RULE_BOOL(NPC, DisableLastNames, false, "Enable to disable NPC Last Names")
|
||||
RULE_BOOL(NPC, NPCIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||
RULE_INT(NPC, NPCHasteCap, 150, "Haste cap for non-v3(over haste) haste")
|
||||
RULE_INT(NPC, NPCHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Aggro)
|
||||
@@ -688,6 +706,7 @@ RULE_BOOL(Aggro, UndeadAlwaysAggro, true, "should undead always aggro?")
|
||||
RULE_INT(Aggro, BardAggroCap, 40, "per song bard aggro cap.")
|
||||
RULE_INT(Aggro, InitialAggroBonus, 100, "Initial Aggro Bonus, Default: 100")
|
||||
RULE_INT(Aggro, InitialPetAggroBonus, 100, "Initial Pet Aggro Bonus, Default 100")
|
||||
RULE_STRING(Aggro, ExcludedFleeAllyFactionIDs, "0|5013|5014|5023|5032", "Common Faction IDs that are excluded from faction checks in EntityList::FleeAllyCount")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(TaskSystem)
|
||||
@@ -702,6 +721,7 @@ RULE_BOOL(TaskSystem, ExpRewardsIgnoreLevelBasedEXPMods, false, "Rewarding Level
|
||||
RULE_INT(TaskSystem, SharedTasksWorldProcessRate, 6000, "Timer interval (milliseconds) that shared tasks are processed in world")
|
||||
RULE_INT(TaskSystem, SharedTasksTerminateTimerMS, 120000, "Delay (milliseconds) until a shared task is terminated if requirements are no longer met after member removal (default: 2 minutes)")
|
||||
RULE_BOOL(TaskSystem, UpdateOneElementPerTask, true, "If true (live-like) task updates only increment the first matching activity. If false all matching elements will be incremented.")
|
||||
RULE_INT(TaskSystem, MaxUpdateMessages, 50, "Maximum update messages for non-GiveCash activity types in IncrementDoneCount")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Range)
|
||||
@@ -713,6 +733,7 @@ RULE_INT(Range, SpellParticles, 135, "The packet range in which spell particles
|
||||
RULE_INT(Range, DamageMessages, 50, "The packet range in which damage messages are sent (non-crit)")
|
||||
RULE_INT(Range, SpellMessages, 75, "The packet range in which spell damage messages are sent")
|
||||
RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are sent")
|
||||
RULE_INT(Range, StunMessages, 75, "The packet range in which stun messages are sent")
|
||||
RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients")
|
||||
RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent")
|
||||
RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance")
|
||||
@@ -754,6 +775,9 @@ RULE_BOOL(Bots, CazicTouchBotsOwner, true, "Default True. Cazic Touch/DT will hi
|
||||
RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.")
|
||||
RULE_BOOL(Bots, BotsCanClickItems, true, "Enables the ability for bots to click items they have equipped. Default TRUE")
|
||||
RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to click Mage Epic 1.0. Default TRUE")
|
||||
RULE_BOOL(Bots, BotsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||
RULE_INT(Bots, BotsHasteCap, 100, "Haste cap for non-v3(over haste) haste")
|
||||
RULE_INT(Bots, BotsHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Chat)
|
||||
@@ -800,6 +824,7 @@ RULE_INT(Bazaar, MaxBarterSearchResults, 200, "The maximum results returned in t
|
||||
RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a bazaar purchase as a percentage of item cost. Default is 20% of item cost. RoF+ Only.")
|
||||
RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.")
|
||||
RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.")
|
||||
RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mail)
|
||||
@@ -891,6 +916,7 @@ RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any w
|
||||
RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting")
|
||||
RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow")
|
||||
RULE_INT(Inventory, AlternateAugmentationSealer, 53, "Allows RoF+ clients to augment items from a special container type")
|
||||
RULE_BOOL(Inventory, LazyLoadBank, true, "Don't load bank during zoning, only when in proximinity to a banker. May increase zone speed and stability")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Client)
|
||||
@@ -993,6 +1019,8 @@ RULE_BOOL(Items, DisableNoRent, false, "Enable this to disable No Rent Items")
|
||||
RULE_BOOL(Items, DisableNoTransfer, false, "Enable this to disable No Transfer Items")
|
||||
RULE_BOOL(Items, DisablePotionBelt, false, "Enable this to disable Potion Belt Items")
|
||||
RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell Focus Effects on Items")
|
||||
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_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Parcel)
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
|
||||
#define ServerOP_TraderMessaging 0x0120
|
||||
#define ServerOP_BazaarPurchase 0x0121
|
||||
#define ServerOP_BuyerMessaging 0x0122
|
||||
|
||||
#define ServerOP_InstanceUpdateTime 0x014F
|
||||
#define ServerOP_AdventureRequest 0x0150
|
||||
|
||||
+361
-411
@@ -46,6 +46,9 @@
|
||||
#include "repositories/character_item_recast_repository.h"
|
||||
#include "repositories/character_corpses_repository.h"
|
||||
#include "repositories/skill_caps_repository.h"
|
||||
#include "repositories/inventory_repository.h"
|
||||
#include "repositories/books_repository.h"
|
||||
#include "repositories/sharedbank_repository.h"
|
||||
|
||||
namespace ItemField
|
||||
{
|
||||
@@ -188,242 +191,268 @@ SharedDatabase::MailKeys SharedDatabase::GetMailKey(int character_id)
|
||||
return MailKeys{};
|
||||
}
|
||||
|
||||
bool SharedDatabase::SaveCursor(uint32 char_id, std::list<EQ::ItemInstance*>::const_iterator &start, std::list<EQ::ItemInstance*>::const_iterator &end)
|
||||
bool SharedDatabase::SaveCursor(
|
||||
uint32 char_id,
|
||||
std::list<EQ::ItemInstance*>::const_iterator& start,
|
||||
std::list<EQ::ItemInstance*>::const_iterator& end
|
||||
)
|
||||
{
|
||||
// Delete cursor items
|
||||
const std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i "
|
||||
"AND ((slotid >= 8000 AND slotid <= 8999) "
|
||||
"OR slotid = %i OR (slotid >= %i AND slotid <= %i) )",
|
||||
char_id, EQ::invslot::slotCursor,
|
||||
EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END);
|
||||
const auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
std::cout << "Clearing cursor failed: " << results.ErrorMessage() << std::endl;
|
||||
return false;
|
||||
}
|
||||
const int deleted = InventoryRepository::DeleteWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`character_id` = {} AND (`slot_id` = {} OR `slot_id` BETWEEN {} AND {})",
|
||||
char_id,
|
||||
EQ::invslot::slotCursor,
|
||||
EQ::invbag::CURSOR_BAG_BEGIN,
|
||||
EQ::invbag::CURSOR_BAG_END
|
||||
)
|
||||
);
|
||||
|
||||
int i = 8000;
|
||||
for(auto& it = start; it != end; ++it, i++) {
|
||||
if (i > 8999) { break; } // shouldn't be anything in the queue that indexes this high
|
||||
const EQ::ItemInstance *inst = *it;
|
||||
const int16 use_slot = (i == 8000) ? EQ::invslot::slotCursor : i;
|
||||
int16 i = EQ::invslot::slotCursor;
|
||||
for (auto& it = start; it != end; ++it, i++) {
|
||||
// shouldn't be anything in the queue that indexes this high
|
||||
if (i > EQ::invbag::CURSOR_BAG_END) {
|
||||
break;
|
||||
}
|
||||
|
||||
const EQ::ItemInstance* inst = *it;
|
||||
const int16 use_slot = i == EQ::invslot::slotCursor ? EQ::invslot::slotCursor : i;
|
||||
if (!SaveInventory(char_id, inst, use_slot)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const EQ::ItemInstance* inst)
|
||||
{
|
||||
// Delete cursor items
|
||||
const std::string query = StringFormat("SELECT itemid, charges FROM sharedbank "
|
||||
"WHERE acctid = %d AND slotid = %d",
|
||||
account_id, slot_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
//returning true is less harmful in the face of a query error
|
||||
return true;
|
||||
if (!inst || !inst->GetItem()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0)
|
||||
return false;
|
||||
const auto& l = SharedbankRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`account_id` = {} AND `slot_id` = {} LIMIT 1",
|
||||
account_id,
|
||||
slot_id
|
||||
)
|
||||
);
|
||||
|
||||
auto& row = results.begin();
|
||||
if (l.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 id = Strings::ToUnsignedInt(row[0]);
|
||||
const uint16 charges = Strings::ToUnsignedInt(row[1]);
|
||||
const auto& e = l.front();
|
||||
|
||||
uint16 expect_charges;
|
||||
uint16 expect_charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits<int16>::max();
|
||||
|
||||
if(inst->GetCharges() >= 0)
|
||||
expect_charges = inst->GetCharges();
|
||||
else
|
||||
expect_charges = 0x7FFF;
|
||||
|
||||
if(id != inst->GetItem()->ID || charges != expect_charges)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return e.item_id == inst->GetID() && e.charges == expect_charges;
|
||||
}
|
||||
|
||||
bool SharedDatabase::SaveInventory(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) {
|
||||
|
||||
//never save tribute slots:
|
||||
if (slot_id >= EQ::invslot::TRIBUTE_BEGIN && slot_id <= EQ::invslot::TRIBUTE_END)
|
||||
return true;
|
||||
if (slot_id >= EQ::invslot::GUILD_TRIBUTE_BEGIN && slot_id <= EQ::invslot::GUILD_TRIBUTE_END)
|
||||
bool SharedDatabase::SaveInventory(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id)
|
||||
{
|
||||
// Don't save any Tribute slots
|
||||
if (
|
||||
EQ::ValueWithin(slot_id, EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END) ||
|
||||
EQ::ValueWithin(slot_id, EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (slot_id >= EQ::invslot::SHARED_BANK_BEGIN && slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) {
|
||||
// Shared bank inventory
|
||||
if (
|
||||
EQ::ValueWithin(slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) ||
|
||||
EQ::ValueWithin(slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END)
|
||||
) {
|
||||
if (!inst) {
|
||||
return DeleteSharedBankSlot(char_id, slot_id);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Needed to clear out bag slots that 'REPLACE' in UpdateSharedBankSlot does not overwrite..otherwise, duplication occurs
|
||||
// (This requires that parent then child items be sent..which should be how they are currently passed)
|
||||
if (EQ::InventoryProfile::SupportsContainers(slot_id))
|
||||
if (EQ::InventoryProfile::SupportsContainers(slot_id)) {
|
||||
DeleteSharedBankSlot(char_id, slot_id);
|
||||
}
|
||||
|
||||
return UpdateSharedBankSlot(char_id, inst, slot_id);
|
||||
}
|
||||
}
|
||||
else if (!inst) { // All other inventory
|
||||
} else if (!inst) { // All other inventory
|
||||
return DeleteInventorySlot(char_id, slot_id);
|
||||
}
|
||||
|
||||
// Needed to clear out bag slots that 'REPLACE' in UpdateInventorySlot does not overwrite..otherwise, duplication occurs
|
||||
// (This requires that parent then child items be sent..which should be how they are currently passed)
|
||||
if (EQ::InventoryProfile::SupportsContainers(slot_id))
|
||||
if (EQ::InventoryProfile::SupportsContainers(slot_id)) {
|
||||
DeleteInventorySlot(char_id, slot_id);
|
||||
return UpdateInventorySlot(char_id, inst, slot_id);
|
||||
}
|
||||
|
||||
bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) {
|
||||
// need to check 'inst' argument for valid pointer
|
||||
|
||||
uint32 augslot[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 };
|
||||
if (inst->IsClassCommon()) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
const EQ::ItemInstance *auginst = inst->GetItem(i);
|
||||
augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 charges;
|
||||
if(inst->GetCharges() >= 0)
|
||||
charges = inst->GetCharges();
|
||||
else
|
||||
charges = 0x7FFF;
|
||||
|
||||
// Update/Insert item
|
||||
const std::string query = StringFormat("REPLACE INTO inventory "
|
||||
"(charid, slotid, itemid, charges, instnodrop, custom_data, color, "
|
||||
"augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model) "
|
||||
"VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, "
|
||||
"%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)",
|
||||
static_cast<unsigned long>(char_id), static_cast<unsigned long>(slot_id), static_cast<unsigned long>(inst->GetItem()->ID),
|
||||
static_cast<unsigned long>(charges), static_cast<unsigned long>(inst->IsAttuned() ? 1 : 0),
|
||||
inst->GetCustomDataString().c_str(), static_cast<unsigned long>(inst->GetColor()),
|
||||
static_cast<unsigned long>(augslot[0]), static_cast<unsigned long>(augslot[1]), static_cast<unsigned long>(augslot[2]),
|
||||
static_cast<unsigned long>(augslot[3]), static_cast<unsigned long>(augslot[4]), static_cast<unsigned long>(augslot[5]), static_cast<unsigned long>(inst->GetOrnamentationIcon()),
|
||||
static_cast<unsigned long>(inst->GetOrnamentationIDFile()), static_cast<unsigned long>(inst->GetOrnamentHeroModel()));
|
||||
const auto results = QueryDatabase(query);
|
||||
|
||||
// Save bag contents, if slot supports bag contents
|
||||
if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id))
|
||||
// Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID'
|
||||
// messages through attrition (and the modded code in SaveInventory)
|
||||
for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx < inst->GetItem()->BagSlots && idx <= EQ::invbag::SLOT_END; idx++) {
|
||||
const EQ::ItemInstance* baginst = inst->GetItem(idx);
|
||||
SaveInventory(char_id, baginst, EQ::InventoryProfile::CalcSlotId(slot_id, idx));
|
||||
}
|
||||
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return UpdateInventorySlot(char_id, inst, slot_id);
|
||||
}
|
||||
|
||||
bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) {
|
||||
// need to check 'inst' argument for valid pointer
|
||||
|
||||
uint32 augslot[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 };
|
||||
if (inst->IsClassCommon()) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
const EQ::ItemInstance *auginst = inst->GetItem(i);
|
||||
augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0;
|
||||
}
|
||||
bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id)
|
||||
{
|
||||
if (!inst || !inst->GetItem()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update/Insert item
|
||||
const uint32 account_id = GetAccountIDByChar(char_id);
|
||||
uint16 charges;
|
||||
if(inst->GetCharges() >= 0)
|
||||
charges = inst->GetCharges();
|
||||
else
|
||||
charges = 0x7FFF;
|
||||
std::vector<uint32> augment_ids = inst->GetAugmentIDs();
|
||||
|
||||
const std::string query = StringFormat("REPLACE INTO sharedbank "
|
||||
"(acctid, slotid, itemid, charges, custom_data, "
|
||||
"augslot1, augslot2, augslot3, augslot4, augslot5, augslot6) "
|
||||
"VALUES( %lu, %lu, %lu, %lu, '%s', "
|
||||
"%lu, %lu, %lu, %lu, %lu, %lu)",
|
||||
static_cast<unsigned long>(account_id), static_cast<unsigned long>(slot_id), static_cast<unsigned long>(inst->GetItem()->ID),
|
||||
static_cast<unsigned long>(charges), inst->GetCustomDataString().c_str(), static_cast<unsigned long>(augslot[0]),
|
||||
static_cast<unsigned long>(augslot[1]), static_cast<unsigned long>(augslot[2]), static_cast<unsigned long>(augslot[3]), static_cast<unsigned long>(augslot[4]),
|
||||
static_cast<unsigned long>(augslot[5]));
|
||||
const auto results = QueryDatabase(query);
|
||||
uint16 charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits<int16>::max();
|
||||
|
||||
// Save bag contents, if slot supports bag contents
|
||||
auto e = InventoryRepository::NewEntity();
|
||||
|
||||
e.character_id = char_id;
|
||||
e.slot_id = slot_id;
|
||||
e.item_id = inst->GetID();
|
||||
e.charges = charges;
|
||||
e.color = inst->GetColor();
|
||||
e.augment_one = augment_ids[0];
|
||||
e.augment_two = augment_ids[1];
|
||||
e.augment_three = augment_ids[2];
|
||||
e.augment_four = augment_ids[3];
|
||||
e.augment_five = augment_ids[4];
|
||||
e.augment_six = augment_ids[5];
|
||||
e.instnodrop = inst->IsAttuned() ? 1 : 0;
|
||||
e.custom_data = inst->GetCustomDataString();
|
||||
e.ornament_icon = inst->GetOrnamentationIcon();
|
||||
e.ornament_idfile = inst->GetOrnamentationIDFile();
|
||||
e.ornament_hero_model = inst->GetOrnamentHeroModel();
|
||||
e.guid = inst->GetSerialNumber();
|
||||
|
||||
const int replaced = InventoryRepository::ReplaceOne(*this, e);
|
||||
|
||||
// Save bag contents, if slot supports bag contents
|
||||
if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) {
|
||||
// Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID'
|
||||
// messages through attrition (and the modded code in SaveInventory)
|
||||
for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx < inst->GetItem()->BagSlots && idx <= EQ::invbag::SLOT_END; idx++) {
|
||||
const EQ::ItemInstance* baginst = inst->GetItem(idx);
|
||||
SaveInventory(char_id, baginst, EQ::InventoryProfile::CalcSlotId(slot_id, idx));
|
||||
for (
|
||||
uint8 i = EQ::invbag::SLOT_BEGIN;
|
||||
i < inst->GetItem()->BagSlots && i <= EQ::invbag::SLOT_END;
|
||||
i++
|
||||
) {
|
||||
const EQ::ItemInstance* bag_inst = inst->GetItem(i);
|
||||
SaveInventory(char_id, bag_inst, EQ::InventoryProfile::CalcSlotId(slot_id, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return replaced;
|
||||
}
|
||||
|
||||
bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) {
|
||||
bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id)
|
||||
{
|
||||
if (!inst || !inst->GetItem()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete item
|
||||
std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid = %i", char_id, slot_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint32> augment_ids = inst->GetAugmentIDs();
|
||||
|
||||
// Delete bag slots, if need be
|
||||
if (!EQ::InventoryProfile::SupportsContainers(slot_id))
|
||||
return true;
|
||||
uint16 charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits<int16>::max();
|
||||
|
||||
const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN);
|
||||
query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid >= %i AND slotid < %i",
|
||||
char_id, base_slot_id, (base_slot_id+10));
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
const uint32 account_id = GetAccountIDByChar(char_id);
|
||||
|
||||
// @merth: need to delete augments here
|
||||
return true;
|
||||
auto e = SharedbankRepository::NewEntity();
|
||||
|
||||
e.account_id = account_id;
|
||||
e.slot_id = slot_id;
|
||||
e.item_id = inst->GetID();
|
||||
e.charges = charges;
|
||||
e.color = inst->GetColor();
|
||||
e.augment_one = augment_ids[0];
|
||||
e.augment_two = augment_ids[1];
|
||||
e.augment_three = augment_ids[2];
|
||||
e.augment_four = augment_ids[3];
|
||||
e.augment_five = augment_ids[4];
|
||||
e.augment_six = augment_ids[5];
|
||||
e.custom_data = inst->GetCustomDataString();
|
||||
e.ornament_icon = inst->GetOrnamentationIcon();
|
||||
e.ornament_idfile = inst->GetOrnamentationIDFile();
|
||||
e.ornament_hero_model = inst->GetOrnamentHeroModel();
|
||||
e.guid = inst->GetSerialNumber();
|
||||
|
||||
const int replaced = SharedbankRepository::ReplaceOne(*this, e);
|
||||
|
||||
// Save bag contents, if slot supports bag contents
|
||||
if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) {
|
||||
// Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID'
|
||||
// messages through attrition (and the modded code in SaveInventory)
|
||||
for (
|
||||
uint8 i = EQ::invbag::SLOT_BEGIN;
|
||||
i < inst->GetItem()->BagSlots && i <= EQ::invbag::SLOT_END;
|
||||
i++
|
||||
) {
|
||||
const EQ::ItemInstance* bag_inst = inst->GetItem(i);
|
||||
SaveInventory(char_id, bag_inst, EQ::InventoryProfile::CalcSlotId(slot_id, i));
|
||||
}
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) {
|
||||
bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id)
|
||||
{
|
||||
const int deleted = InventoryRepository::DeleteWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`character_id` = {} AND `slot_id` = {}",
|
||||
char_id,
|
||||
slot_id
|
||||
)
|
||||
);
|
||||
|
||||
// Delete item
|
||||
const uint32 account_id = GetAccountIDByChar(char_id);
|
||||
std::string query = StringFormat("DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", account_id, slot_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
if (!deleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete bag slots, if need be
|
||||
if (!EQ::InventoryProfile::SupportsContainers(slot_id))
|
||||
return true;
|
||||
if (!EQ::InventoryProfile::SupportsContainers(slot_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN);
|
||||
query = StringFormat("DELETE FROM sharedbank WHERE acctid = %i "
|
||||
"AND slotid >= %i AND slotid < %i",
|
||||
account_id, base_slot_id, (base_slot_id+10));
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN);
|
||||
|
||||
// @merth: need to delete augments here
|
||||
return true;
|
||||
return InventoryRepository::DeleteWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`character_id` = {} AND `slot_id` BETWEEN {} AND {}",
|
||||
char_id,
|
||||
base_slot_id,
|
||||
base_slot_id + (EQ::invbag::SLOT_COUNT - 1)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id)
|
||||
{
|
||||
const uint32 account_id = GetAccountIDByChar(char_id);
|
||||
|
||||
const int deleted = SharedbankRepository::DeleteWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`account_id` = {} AND `slot_id` = {}",
|
||||
account_id,
|
||||
slot_id
|
||||
)
|
||||
);
|
||||
|
||||
if (!deleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EQ::InventoryProfile::SupportsContainers(slot_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN);
|
||||
|
||||
return SharedbankRepository::DeleteWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`account_id` = {} AND `slotid` BETWEEN {} AND {}",
|
||||
account_id,
|
||||
base_slot_id,
|
||||
base_slot_id + (EQ::invbag::SLOT_COUNT - 1)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -548,96 +577,81 @@ bool SharedDatabase::SetStartingItems(
|
||||
// Retrieve shared bank inventory based on either account or character
|
||||
bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid)
|
||||
{
|
||||
std::string query;
|
||||
const uint32 account_id = is_charid ? GetAccountIDByChar(id) : id;
|
||||
|
||||
if (is_charid) {
|
||||
query = fmt::format(
|
||||
"SELECT sb.slotid, sb.itemid, sb.charges, "
|
||||
"sb.augslot1, sb.augslot2, sb.augslot3, "
|
||||
"sb.augslot4, sb.augslot5, sb.augslot6, sb.custom_data "
|
||||
"FROM sharedbank sb INNER JOIN character_data ch "
|
||||
"ON ch.account_id = sb.acctid WHERE ch.id = {} ORDER BY sb.slotid",
|
||||
id
|
||||
);
|
||||
} else {
|
||||
query = fmt::format(
|
||||
"SELECT slotid, itemid, charges, "
|
||||
"augslot1, augslot2, augslot3, "
|
||||
"augslot4, augslot5, augslot6, custom_data "
|
||||
"FROM sharedbank WHERE acctid = {} ORDER BY slotid",
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
// If we have no results we still need to return true
|
||||
if (!results.Success()) {
|
||||
if (!account_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto row : results) {
|
||||
int16 slot_id = static_cast<int16>(Strings::ToInt(row[0]));
|
||||
uint32 item_id = Strings::ToUnsignedInt(row[1]);
|
||||
const int16 charges = static_cast<int16>(Strings::ToInt(row[2]));
|
||||
const auto& l = SharedbankRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`account_id` = {}",
|
||||
account_id
|
||||
)
|
||||
);
|
||||
|
||||
uint32 aug[EQ::invaug::SOCKET_COUNT];
|
||||
aug[0] = Strings::ToUnsignedInt(row[3]);
|
||||
aug[1] = Strings::ToUnsignedInt(row[4]);
|
||||
aug[2] = Strings::ToUnsignedInt(row[5]);
|
||||
aug[3] = Strings::ToUnsignedInt(row[6]);
|
||||
aug[4] = Strings::ToUnsignedInt(row[7]);
|
||||
aug[5] = Strings::ToUnsignedInt(row[8]);
|
||||
if (l.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const EQ::ItemData *item = GetItem(item_id);
|
||||
for (const auto& e : l) {
|
||||
uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = {
|
||||
e.augment_one,
|
||||
e.augment_two,
|
||||
e.augment_three,
|
||||
e.augment_four,
|
||||
e.augment_five,
|
||||
e.augment_six
|
||||
};
|
||||
|
||||
const EQ::ItemData* item = GetItem(e.item_id);
|
||||
|
||||
if (!item) {
|
||||
LogError(
|
||||
"Warning: [{}] [{}] has an invalid item_id [{}] in inventory slot [{}]",
|
||||
is_charid ? "charid" : "acctid",
|
||||
"Warning: {} [{}] has an invalid item_id [{}] in slot_id [{}]",
|
||||
is_charid ? "character_id" : "account_id",
|
||||
id,
|
||||
item_id,
|
||||
slot_id
|
||||
e.item_id,
|
||||
e.slot_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto inst = CreateBaseItem(item, charges);
|
||||
EQ::ItemInstance* inst = CreateBaseItem(item, e.charges);
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inst && item->IsClassCommon()) {
|
||||
if (item->IsClassCommon()) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
if (aug[i]) {
|
||||
inst->PutAugment(this, i, aug[i]);
|
||||
if (augment_ids[i]) {
|
||||
inst->PutAugment(this, i, augment_ids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inst && row[9]) {
|
||||
std::string data_str(row[9]);
|
||||
inst->SetCustomDataString(data_str);
|
||||
if (!e.custom_data.empty()) {
|
||||
inst->SetCustomDataString(e.custom_data);
|
||||
}
|
||||
|
||||
// theoretically inst can be nullptr ... this would be very bad ...
|
||||
const int16 put_slot_id = inv->PutItem(slot_id, *inst);
|
||||
const int16 put_slot_id = inv->PutItem(e.slot_id, *inst);
|
||||
safe_delete(inst);
|
||||
|
||||
// Save ptr to item in inventory
|
||||
if (put_slot_id != INVALID_INDEX) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LogError(
|
||||
"Warning: Invalid slot_id for item in shared bank inventory: [{}]=[{}], item_id=[{}], slot_id=[{}]",
|
||||
is_charid ? "charid" : "acctid",
|
||||
"Warning: Invalid slot_id for item in shared bank inventory for {} [{}] item_id [{}] slot_id [{}]",
|
||||
is_charid ? "character_id" : "account_id",
|
||||
id,
|
||||
item_id,
|
||||
slot_id
|
||||
e.item_id,
|
||||
e.slot_id
|
||||
);
|
||||
|
||||
if (is_charid) {
|
||||
SaveInventory(id, nullptr, slot_id);
|
||||
SaveInventory(id, nullptr, e.slot_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,54 +659,78 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is
|
||||
}
|
||||
|
||||
// Overloaded: Retrieve character inventory based on character id (zone entry)
|
||||
bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile* inv)
|
||||
{
|
||||
if (!char_id || !inv)
|
||||
return false;
|
||||
|
||||
// Retrieve character inventory
|
||||
const std::string query =
|
||||
StringFormat("SELECT slotid, itemid, charges, color, augslot1, augslot2, augslot3, augslot4, augslot5, "
|
||||
"augslot6, instnodrop, custom_data, ornamenticon, ornamentidfile, ornament_hero_model FROM "
|
||||
"inventory WHERE charid = %i ORDER BY slotid",
|
||||
char_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogError("If you got an error related to the 'instnodrop' field, run the "
|
||||
"following SQL Queries:\nalter table inventory add instnodrop "
|
||||
"tinyint(1) unsigned default 0 not null;\n");
|
||||
if (!char_id || !inv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto timestamps = GetItemRecastTimestamps(char_id);
|
||||
// Retrieve character inventory
|
||||
auto results = InventoryRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`character_id` = '{}' ORDER BY `slot_id`",
|
||||
char_id
|
||||
)
|
||||
);
|
||||
|
||||
auto cv_conflict = false;
|
||||
const auto pmask = inv->GetLookup()->PossessionsBitmask;
|
||||
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
|
||||
if (results.empty()) {
|
||||
LogError("Error loading inventory for char_id {} from the database.", char_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
||||
int16 slot_id = Strings::ToInt(row[0]);
|
||||
for (auto const& row: results) {
|
||||
if (row.guid != 0) {
|
||||
EQ::ItemInstance::AddGUIDToMap(row.guid);
|
||||
}
|
||||
}
|
||||
|
||||
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { // Titanium thru UF check
|
||||
const auto timestamps = GetItemRecastTimestamps(char_id);
|
||||
auto cv_conflict = false;
|
||||
const auto pmask = inv->GetLookup()->PossessionsBitmask;
|
||||
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
|
||||
|
||||
std::vector<InventoryRepository::Inventory> queue{ };
|
||||
for (auto& row: results) {
|
||||
const int16 slot_id = row.slot_id;
|
||||
const uint32 item_id = row.item_id;
|
||||
const uint16 charges = row.charges;
|
||||
const uint32 color = row.color;
|
||||
const bool instnodrop = row.instnodrop;
|
||||
const uint32 ornament_icon = row.ornament_icon;
|
||||
const uint32 ornament_idfile = row.ornament_idfile;
|
||||
const uint32 ornament_hero_model = row.ornament_hero_model;
|
||||
|
||||
uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = {
|
||||
row.augment_one,
|
||||
row.augment_two,
|
||||
row.augment_three,
|
||||
row.augment_four,
|
||||
row.augment_five,
|
||||
row.augment_six
|
||||
};
|
||||
|
||||
if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) {
|
||||
// Titanium thru UF check
|
||||
if (((static_cast<uint64>(1) << slot_id) & pmask) == 0) {
|
||||
cv_conflict = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { // Titanium thru UF check
|
||||
} else if (EQ::ValueWithin(slot_id, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) {
|
||||
// Titanium thru UF check
|
||||
const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
||||
if (((static_cast<uint64>(1) << parent_slot) & pmask) == 0) {
|
||||
cv_conflict = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { // Titanium check
|
||||
} else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) {
|
||||
// Titanium check
|
||||
if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) {
|
||||
cv_conflict = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { // Titanium check
|
||||
} else if (EQ::ValueWithin(slot_id, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) {
|
||||
// Titanium check
|
||||
const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
||||
if (parent_index >= bank_size) {
|
||||
cv_conflict = true;
|
||||
@@ -700,102 +738,98 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
}
|
||||
}
|
||||
|
||||
uint32 item_id = Strings::ToUnsignedInt(row[1]);
|
||||
const uint16 charges = Strings::ToUnsignedInt(row[2]);
|
||||
const uint32 color = Strings::ToUnsignedInt(row[3]);
|
||||
|
||||
uint32 aug[EQ::invaug::SOCKET_COUNT];
|
||||
|
||||
aug[0] = Strings::ToUnsignedInt(row[4]);
|
||||
aug[1] = Strings::ToUnsignedInt(row[5]);
|
||||
aug[2] = Strings::ToUnsignedInt(row[6]);
|
||||
aug[3] = Strings::ToUnsignedInt(row[7]);
|
||||
aug[4] = Strings::ToUnsignedInt(row[8]);
|
||||
aug[5] = Strings::ToUnsignedInt(row[9]);
|
||||
|
||||
const bool instnodrop = (row[10] && static_cast<uint16>(Strings::ToUnsignedInt(row[10])));
|
||||
|
||||
const uint32 ornament_icon = Strings::ToUnsignedInt(row[12]);
|
||||
const uint32 ornament_idfile = Strings::ToUnsignedInt(row[13]);
|
||||
uint32 ornament_hero_model = Strings::ToUnsignedInt(row[14]);
|
||||
|
||||
const EQ::ItemData *item = GetItem(item_id);
|
||||
|
||||
auto* item = GetItem(item_id);
|
||||
if (!item) {
|
||||
LogError("Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", char_id, item_id,
|
||||
slot_id);
|
||||
LogError(
|
||||
"Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]",
|
||||
char_id,
|
||||
item_id,
|
||||
slot_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
EQ::ItemInstance *inst = CreateBaseItem(item, charges);
|
||||
|
||||
if (inst == nullptr)
|
||||
auto* inst = CreateBaseItem(item, charges);
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (row[11]) {
|
||||
std::string data_str(row[11]);
|
||||
inst->SetCustomDataString(data_str);
|
||||
if (!row.custom_data.empty()) {
|
||||
inst->SetCustomDataString(row.custom_data);
|
||||
}
|
||||
|
||||
inst->SetOrnamentIcon(ornament_icon);
|
||||
inst->SetOrnamentationIDFile(ornament_idfile);
|
||||
inst->SetOrnamentHeroModel(item->HerosForgeModel);
|
||||
|
||||
if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END))
|
||||
if (
|
||||
instnodrop ||
|
||||
(
|
||||
inst->GetItem()->Attuneable &&
|
||||
EQ::ValueWithin(slot_id, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END)
|
||||
)
|
||||
) {
|
||||
inst->SetAttuned(true);
|
||||
}
|
||||
|
||||
if (color > 0)
|
||||
if (color > 0) {
|
||||
inst->SetColor(color);
|
||||
}
|
||||
|
||||
if (charges == 0x7FFF)
|
||||
if (charges == std::numeric_limits<int16>::max()) {
|
||||
inst->SetCharges(-1);
|
||||
else if (charges == 0 && inst->IsStackable()) // Stackable items need a minimum charge of 1 remain moveable.
|
||||
} else if (charges == 0 && inst->IsStackable()) {
|
||||
// Stackable items need a minimum charge of 1 remain moveable.
|
||||
inst->SetCharges(1);
|
||||
else
|
||||
} else {
|
||||
inst->SetCharges(charges);
|
||||
}
|
||||
|
||||
if (item->RecastDelay) {
|
||||
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) {
|
||||
inst->SetRecastTimestamp(timestamps.at(item->RecastType));
|
||||
} else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) {
|
||||
inst->SetRecastTimestamp(timestamps.at(item->ID));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
inst->SetRecastTimestamp(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (item->IsClassCommon()) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
if (aug[i])
|
||||
inst->PutAugment(this, i, aug[i]);
|
||||
if (augment_ids[i]) {
|
||||
inst->PutAugment(this, i, augment_ids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16 put_slot_id;
|
||||
if (slot_id >= 8000 && slot_id <= 8999) {
|
||||
put_slot_id = inv->PushCursor(*inst);
|
||||
} else if (slot_id >= 3111 && slot_id <= 3179) {
|
||||
// Admins: please report any occurrences of this error
|
||||
LogError("Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...",
|
||||
char_id, item_id, slot_id);
|
||||
if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END)) {
|
||||
put_slot_id = inv->PushCursor(*inst);
|
||||
} else {
|
||||
put_slot_id = inv->PutItem(slot_id, *inst);
|
||||
}
|
||||
|
||||
row.guid = inst->GetSerialNumber();
|
||||
queue.push_back(row);
|
||||
|
||||
safe_delete(inst);
|
||||
|
||||
// Save ptr to item in inventory
|
||||
if (put_slot_id == INVALID_INDEX) {
|
||||
LogError("Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]",
|
||||
char_id, item_id, slot_id);
|
||||
LogError(
|
||||
"Warning: Invalid slot_id for item in inventory for character_id [{}] item_id [{}] slot_id [{}]",
|
||||
char_id,
|
||||
item_id,
|
||||
slot_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (cv_conflict) {
|
||||
const std::string& char_name = GetCharName(char_id);
|
||||
LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
||||
LogError(
|
||||
"ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
||||
char_name,
|
||||
char_id,
|
||||
EQ::versions::MobVersionName(inv->InventoryVersion()),
|
||||
@@ -803,98 +837,16 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
);
|
||||
}
|
||||
|
||||
if (!queue.empty()) {
|
||||
InventoryRepository::ReplaceMany(*this, queue);
|
||||
}
|
||||
|
||||
EQ::ItemInstance::ClearGUIDMap();
|
||||
|
||||
// Retrieve shared inventory
|
||||
return GetSharedBank(char_id, inv, true);
|
||||
}
|
||||
|
||||
// Overloaded: Retrieve character inventory based on account_id and character name (char select)
|
||||
bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv) // deprecated
|
||||
{
|
||||
// Retrieve character inventory
|
||||
const std::string query =
|
||||
StringFormat("SELECT slotid, itemid, charges, color, augslot1, "
|
||||
"augslot2, augslot3, augslot4, augslot5, augslot6, instnodrop, custom_data, ornamenticon, "
|
||||
"ornamentidfile, ornament_hero_model "
|
||||
"FROM inventory INNER JOIN character_data ch "
|
||||
"ON ch.id = charid WHERE ch.name = '%s' AND ch.account_id = %i ORDER BY slotid",
|
||||
name, account_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogError("If you got an error related to the 'instnodrop' field, run the "
|
||||
"following SQL Queries:\nalter table inventory add instnodrop "
|
||||
"tinyint(1) unsigned default 0 not null;\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
||||
int16 slot_id = Strings::ToInt(row[0]);
|
||||
uint32 item_id = Strings::ToUnsignedInt(row[1]);
|
||||
const int8 charges = Strings::ToInt(row[2]);
|
||||
const uint32 color = Strings::ToUnsignedInt(row[3]);
|
||||
|
||||
uint32 aug[EQ::invaug::SOCKET_COUNT];
|
||||
aug[0] = Strings::ToUnsignedInt(row[4]);
|
||||
aug[1] = Strings::ToUnsignedInt(row[5]);
|
||||
aug[2] = Strings::ToUnsignedInt(row[6]);
|
||||
aug[3] = Strings::ToUnsignedInt(row[7]);
|
||||
aug[4] = Strings::ToUnsignedInt(row[8]);
|
||||
aug[5] = Strings::ToUnsignedInt(row[9]);
|
||||
|
||||
const bool instnodrop = (row[10] && static_cast<uint16>(Strings::ToUnsignedInt(row[10])));
|
||||
const uint32 ornament_icon = Strings::ToUnsignedInt(row[12]);
|
||||
const uint32 ornament_idfile = Strings::ToUnsignedInt(row[13]);
|
||||
uint32 ornament_hero_model = Strings::ToUnsignedInt(row[14]);
|
||||
|
||||
const EQ::ItemData *item = GetItem(item_id);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
EQ::ItemInstance *inst = CreateBaseItem(item, charges);
|
||||
|
||||
if (inst == nullptr)
|
||||
continue;
|
||||
|
||||
inst->SetAttuned(instnodrop);
|
||||
|
||||
if (row[11]) {
|
||||
std::string data_str(row[11]);
|
||||
inst->SetCustomDataString(data_str);
|
||||
}
|
||||
|
||||
inst->SetOrnamentIcon(ornament_icon);
|
||||
inst->SetOrnamentationIDFile(ornament_idfile);
|
||||
inst->SetOrnamentHeroModel(item->HerosForgeModel);
|
||||
|
||||
if (color > 0)
|
||||
inst->SetColor(color);
|
||||
|
||||
inst->SetCharges(charges);
|
||||
|
||||
if (item->IsClassCommon()) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
if (aug[i])
|
||||
inst->PutAugment(this, i, aug[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int16 put_slot_id;
|
||||
if (slot_id >= 8000 && slot_id <= 8999)
|
||||
put_slot_id = inv->PushCursor(*inst);
|
||||
else
|
||||
put_slot_id = inv->PutItem(slot_id, *inst);
|
||||
|
||||
safe_delete(inst);
|
||||
|
||||
// Save ptr to item in inventory
|
||||
if (put_slot_id == INVALID_INDEX)
|
||||
LogError("Warning: Invalid slot_id for item in inventory: name={}, acctid={}, item_id={}, slot_id={}",
|
||||
name, account_id, item_id, slot_id);
|
||||
}
|
||||
|
||||
// Retrieve shared inventory
|
||||
return GetSharedBank(account_id, inv, false);
|
||||
}
|
||||
|
||||
std::map<uint32, uint32> SharedDatabase::GetItemRecastTimestamps(uint32 char_id)
|
||||
{
|
||||
std::map<uint32, uint32> timers;
|
||||
@@ -1246,7 +1198,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
|
||||
|
||||
// Bag
|
||||
item.BagSize = static_cast<uint8>(Strings::ToUnsignedInt(row[ItemField::bagsize]));
|
||||
item.BagSlots = static_cast<uint8>(EQ::Clamp(Strings::ToInt(row[ItemField::bagslots]), 0, 10)); // Will need to be changed from std::min to just use database value when bag slots are increased
|
||||
item.BagSlots = static_cast<uint8>(EQ::Clamp(Strings::ToInt(row[ItemField::bagslots]), 0, static_cast<int>(EQ::invbag::SLOT_COUNT)));
|
||||
item.BagType = static_cast<uint8>(Strings::ToUnsignedInt(row[ItemField::bagtype]));
|
||||
item.BagWR = static_cast<uint8>(EQ::Clamp(Strings::ToInt(row[ItemField::bagwr]), 0, 100));
|
||||
|
||||
@@ -1359,30 +1311,28 @@ const EQ::ItemData* SharedDatabase::IterateItems(uint32* id) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string SharedDatabase::GetBook(const char *txtfile, int16 *language)
|
||||
Book_Struct SharedDatabase::GetBook(const std::string& text_file)
|
||||
{
|
||||
char txtfile2[20];
|
||||
std::string txtout;
|
||||
strcpy(txtfile2, txtfile);
|
||||
const auto& l = BooksRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`name` = '{}'",
|
||||
Strings::Escape(text_file)
|
||||
)
|
||||
);
|
||||
|
||||
const std::string query = StringFormat("SELECT txtfile, language FROM books WHERE name = '%s'", txtfile2);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
txtout.assign(" ",1);
|
||||
return txtout;
|
||||
Book_Struct b;
|
||||
|
||||
if (l.empty()) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
LogError("No book to send, ({})", txtfile);
|
||||
txtout.assign(" ",1);
|
||||
return txtout;
|
||||
}
|
||||
const auto& e = l.front();
|
||||
|
||||
auto& row = results.begin();
|
||||
txtout.assign(row[0],strlen(row[0]));
|
||||
*language = static_cast<int16>(Strings::ToInt(row[1]));
|
||||
b.language = e.language;
|
||||
b.text = e.txtfile;
|
||||
|
||||
return txtout;
|
||||
return b;
|
||||
}
|
||||
|
||||
// Create appropriate EQ::ItemInstance class
|
||||
|
||||
+8
-4
@@ -41,8 +41,7 @@ struct NPCFactionList;
|
||||
struct FactionAssociations;
|
||||
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace EQ {
|
||||
|
||||
struct ItemData;
|
||||
class ItemInstance;
|
||||
@@ -50,6 +49,12 @@ namespace EQ
|
||||
class MemoryMappedFile;
|
||||
}
|
||||
|
||||
struct Book_Struct
|
||||
{
|
||||
uint8 language;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
/*
|
||||
This object is inherited by world and zone's DB object,
|
||||
and is mainly here to facilitate shared memory, and other
|
||||
@@ -99,7 +104,6 @@ public:
|
||||
int32 GetSharedPlatinum(uint32 account_id);
|
||||
bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add);
|
||||
bool GetInventory(uint32 char_id, EQ::InventoryProfile *inv);
|
||||
bool GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv); // deprecated
|
||||
std::map<uint32, uint32> GetItemRecastTimestamps(uint32 char_id);
|
||||
uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type);
|
||||
void ClearOldRecastTimestamps(uint32 char_id);
|
||||
@@ -114,7 +118,7 @@ public:
|
||||
int admin
|
||||
);
|
||||
|
||||
std::string GetBook(const char *txtfile, int16 *language);
|
||||
Book_Struct GetBook(const std::string& text_file);
|
||||
|
||||
/**
|
||||
* items
|
||||
|
||||
@@ -249,7 +249,6 @@ const std::vector<EQ::skills::SkillType>& EQ::skills::GetExtraDamageSkills()
|
||||
EQ::skills::SkillFlyingKick,
|
||||
EQ::skills::SkillKick,
|
||||
EQ::skills::SkillRoundKick,
|
||||
EQ::skills::SkillRoundKick,
|
||||
EQ::skills::SkillTigerClaw,
|
||||
EQ::skills::SkillFrenzy
|
||||
};
|
||||
|
||||
@@ -431,6 +431,16 @@ bool IsCharmSpell(uint16 spell_id)
|
||||
return IsEffectInSpell(spell_id, SE_Charm);
|
||||
}
|
||||
|
||||
bool IsResurrectionSicknessSpell(uint16 spell_id) {
|
||||
return (
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS ||
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS2 ||
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS3 ||
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS4 ||
|
||||
spell_id == SPELL_REVIVAL_SICKNESS
|
||||
);
|
||||
}
|
||||
|
||||
bool IsBlindSpell(uint16 spell_id)
|
||||
{
|
||||
return IsEffectInSpell(spell_id, SE_Blind);
|
||||
@@ -2315,3 +2325,108 @@ bool IsCastNotStandingSpell(uint16 spell_id) {
|
||||
*/
|
||||
return spells[spell_id].cast_not_standing;
|
||||
}
|
||||
|
||||
bool IsAegolismSpell(uint16 spell_id) {
|
||||
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool has_max_hp = false;
|
||||
bool has_current_hp = false;
|
||||
bool has_ac = false;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||
|
||||
if (i == 0 && spells[spell_id].effect_id[i] != SE_StackingCommand_Block) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == 1 && spells[spell_id].effect_id[i] == SE_TotalHP) {
|
||||
has_max_hp = true;
|
||||
}
|
||||
|
||||
if (i == 2 && spells[spell_id].effect_id[i] == SE_CurrentHPOnce) {
|
||||
has_current_hp = true;
|
||||
}
|
||||
|
||||
if (i == 3 && spells[spell_id].effect_id[i] == SE_ArmorClass) {
|
||||
has_ac = true;
|
||||
}
|
||||
|
||||
if (i == 4 && spells[spell_id].effect_id[i] != SE_StackingCommand_Overwrite) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_max_hp && has_current_hp && has_ac) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool AegolismStackingIsSymbolSpell(uint16 spell_id) {
|
||||
|
||||
/*
|
||||
This is hardcoded to be specific to the type of HP buffs that are removed if a mob has an Aegolism buff.
|
||||
*/
|
||||
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool has_max_hp = false;
|
||||
bool has_current_hp = false;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||
|
||||
if ((i < 2 && spells[spell_id].effect_id[i] != SE_CHA) ||
|
||||
i > 3 && spells[spell_id].effect_id[i] != SE_Blank) {
|
||||
return 0;;
|
||||
}
|
||||
|
||||
if (i == 2 && spells[spell_id].effect_id[i] == SE_TotalHP) {
|
||||
has_max_hp = true;
|
||||
}
|
||||
|
||||
if (i == 3 && spells[spell_id].effect_id[i] == SE_CurrentHPOnce) {
|
||||
has_current_hp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_max_hp && has_current_hp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AegolismStackingIsArmorClassSpell(uint16 spell_id) {
|
||||
/*
|
||||
This is hardcoded to be specific to the type of AC buffs that are removed if a mob has an Aegolism buff.
|
||||
*/
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool has_ac = false;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||
|
||||
if ((i < 3 && spells[spell_id].effect_id[i] != SE_CHA) ||
|
||||
i > 3 && spells[spell_id].effect_id[i] != SE_Blank) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == 3 && spells[spell_id].effect_id[i] == SE_ArmorClass) {
|
||||
has_ac = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_ac) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1522,6 +1522,7 @@ bool IsSummonPetSpell(uint16 spell_id);
|
||||
bool IsSummonPCSpell(uint16 spell_id);
|
||||
bool IsPetSpell(uint16 spell_id);
|
||||
bool IsCharmSpell(uint16 spell_id);
|
||||
bool IsResurrectionSicknessSpell(uint16 spell_id);
|
||||
bool IsBlindSpell(uint16 spell_id);
|
||||
bool IsHealthSpell(uint16 spell_id);
|
||||
bool IsCastTimeReductionSpell(uint16 spell_id);
|
||||
@@ -1624,5 +1625,8 @@ bool IsSpellUsableInThisZoneType(uint16 spell_id, uint8 zone_type);
|
||||
const char *GetSpellName(uint16 spell_id);
|
||||
int GetSpellStatValue(uint16 spell_id, const char* stat_identifier, uint8 slot = 0);
|
||||
bool IsCastRestrictedSpell(uint16 spell_id);
|
||||
bool IsAegolismSpell(uint16 spell_id);
|
||||
bool AegolismStackingIsSymbolSpell(uint16 spell_id);
|
||||
bool AegolismStackingIsArmorClassSpell(uint16 spell_id);
|
||||
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.52.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.59.1-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,8 +42,8 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9280
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9288
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -251,4 +251,6 @@ private:
|
||||
scalar m_value;
|
||||
};
|
||||
|
||||
using ref = reference;
|
||||
|
||||
} // namespace perlbind
|
||||
|
||||
@@ -28,8 +28,8 @@ struct pusher
|
||||
++m_pushed;
|
||||
}
|
||||
void push(const std::string& value) { mPUSHp(value.c_str(), value.size()); ++m_pushed; }
|
||||
void push(scalar value) { mPUSHs(value.release()); ++m_pushed; };
|
||||
void push(reference value) { mPUSHs(value.release()); ++m_pushed; };
|
||||
void push(scalar value) { mPUSHs(value.release()); ++m_pushed; }
|
||||
void push(reference value) { mPUSHs(value.release()); ++m_pushed; }
|
||||
|
||||
void push(array value)
|
||||
{
|
||||
@@ -38,7 +38,8 @@ struct pusher
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
// mortalizes one reference to array element to avoid copying
|
||||
PUSHs(sv_2mortal(SvREFCNT_inc(value[i].sv())));
|
||||
SV** sv = av_fetch(static_cast<AV*>(value), i, true);
|
||||
mPUSHs(SvREFCNT_inc(*sv));
|
||||
}
|
||||
m_pushed += count;
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ struct read_as<hash>
|
||||
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
|
||||
{
|
||||
int remaining = items - i;
|
||||
return remaining > 0 && remaining % 2 == 0 && SvTYPE(ST(i)) == SVt_PV;
|
||||
return remaining > 0 && remaining % 2 == 0 && SvTYPE(ST(i)) < SVt_PVAV;
|
||||
}
|
||||
|
||||
static hash get(PerlInterpreter* my_perl, int i, int ax, int items)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
constexpr int perlbind_version_major = 1;
|
||||
constexpr int perlbind_version_minor = 0;
|
||||
constexpr int perlbind_version_minor = 1;
|
||||
constexpr int perlbind_version_patch = 0;
|
||||
|
||||
constexpr int perlbind_version()
|
||||
|
||||
@@ -224,6 +224,23 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) {
|
||||
result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash);
|
||||
|
||||
#ifdef LSPX
|
||||
// if user updated their password on the login server, update it here by validating their credentials with the login server
|
||||
if (!result && db_loginserver == "eqemu") {
|
||||
uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(user, cred);
|
||||
if (account_id > 0) {
|
||||
auto encryption_mode = server.options.GetEncryptionMode();
|
||||
server.db->UpdateLoginserverAccountPasswordHash(
|
||||
user,
|
||||
db_loginserver,
|
||||
eqcrypt_hash(user, cred, encryption_mode)
|
||||
);
|
||||
LogInfo("Updating eqemu account [{}] password hash", account_id);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LogDebug("Success [{0}]", (result ? "true" : "false"));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -138,6 +138,13 @@ public:
|
||||
*/
|
||||
unsigned int GetPlaySequence() const { return m_play_sequence_id; }
|
||||
|
||||
/**
|
||||
* Gets the client version
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
LSClientVersion GetClientVersion() const { return m_client_version; }
|
||||
|
||||
/**
|
||||
* Gets the connection for this client
|
||||
*
|
||||
|
||||
+119
-12
@@ -7,6 +7,79 @@ extern bool run_server;
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "../common/misc.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/file.h"
|
||||
|
||||
void CheckTitaniumOpcodeFile(const std::string &path) {
|
||||
if (File::Exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto f = fopen(path.c_str(), "w");
|
||||
if (f) {
|
||||
fprintf(f, "#EQEmu Public Login Server OPCodes\n");
|
||||
fprintf(f, "OP_SessionReady=0x0001\n");
|
||||
fprintf(f, "OP_Login=0x0002\n");
|
||||
fprintf(f, "OP_ServerListRequest=0x0004\n");
|
||||
fprintf(f, "OP_PlayEverquestRequest=0x000d\n");
|
||||
fprintf(f, "OP_PlayEverquestResponse=0x0021\n");
|
||||
fprintf(f, "OP_ChatMessage=0x0016\n");
|
||||
fprintf(f, "OP_LoginAccepted=0x0017\n");
|
||||
fprintf(f, "OP_ServerListResponse=0x0018\n");
|
||||
fprintf(f, "OP_Poll=0x0029\n");
|
||||
fprintf(f, "OP_EnterChat=0x000f\n");
|
||||
fprintf(f, "OP_PollResponse=0x0011\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckSoDOpcodeFile(const std::string& path) {
|
||||
if (File::Exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto f = fopen(path.c_str(), "w");
|
||||
if (f) {
|
||||
fprintf(f, "#EQEmu Public Login Server OPCodes\n");
|
||||
fprintf(f, "OP_SessionReady=0x0001\n");
|
||||
fprintf(f, "OP_Login=0x0002\n");
|
||||
fprintf(f, "OP_ServerListRequest=0x0004\n");
|
||||
fprintf(f, "OP_PlayEverquestRequest=0x000d\n");
|
||||
fprintf(f, "OP_PlayEverquestResponse=0x0022\n");
|
||||
fprintf(f, "OP_ChatMessage=0x0017\n");
|
||||
fprintf(f, "OP_LoginAccepted=0x0018\n");
|
||||
fprintf(f, "OP_ServerListResponse=0x0019\n");
|
||||
fprintf(f, "OP_Poll=0x0029\n");
|
||||
fprintf(f, "OP_LoginExpansionPacketData=0x0031\n");
|
||||
fprintf(f, "OP_EnterChat=0x000f\n");
|
||||
fprintf(f, "OP_PollResponse=0x0011\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckLarionOpcodeFile(const std::string& path) {
|
||||
if (File::Exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto f = fopen(path.c_str(), "w");
|
||||
if (f) {
|
||||
fprintf(f, "#EQEmu Public Login Server OPCodes\n");
|
||||
fprintf(f, "OP_SessionReady=0x0001\n");
|
||||
fprintf(f, "OP_Login=0x0002\n");
|
||||
fprintf(f, "OP_ServerListRequest=0x0004\n");
|
||||
fprintf(f, "OP_PlayEverquestRequest=0x000d\n");
|
||||
fprintf(f, "OP_PlayEverquestResponse=0x0022\n");
|
||||
fprintf(f, "OP_ChatMessage=0x0017\n");
|
||||
fprintf(f, "OP_LoginAccepted=0x0018\n");
|
||||
fprintf(f, "OP_ServerListResponse=0x0019\n");
|
||||
fprintf(f, "OP_Poll=0x0029\n");
|
||||
fprintf(f, "OP_EnterChat=0x000f\n");
|
||||
fprintf(f, "OP_PollResponse=0x0011\n");
|
||||
fprintf(f, "OP_SystemFingerprint=0x0016\n");
|
||||
fprintf(f, "OP_ExpansionList=0x0030\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
ClientManager::ClientManager()
|
||||
{
|
||||
@@ -19,14 +92,12 @@ ClientManager::ClientManager()
|
||||
|
||||
std::string opcodes_path = fmt::format(
|
||||
"{}/{}",
|
||||
path.GetServerPath(),
|
||||
server.config.GetVariableString(
|
||||
"client_configuration",
|
||||
"titanium_opcodes",
|
||||
"login_opcodes.conf"
|
||||
)
|
||||
path.GetOpcodePath(),
|
||||
"login_opcodes.conf"
|
||||
);
|
||||
|
||||
CheckTitaniumOpcodeFile(opcodes_path);
|
||||
|
||||
if (!titanium_ops->LoadOpcodes(opcodes_path.c_str())) {
|
||||
LogError(
|
||||
"ClientManager fatal error: couldn't load opcodes for Titanium file [{0}]",
|
||||
@@ -58,14 +129,12 @@ ClientManager::ClientManager()
|
||||
|
||||
opcodes_path = fmt::format(
|
||||
"{}/{}",
|
||||
path.GetServerPath(),
|
||||
server.config.GetVariableString(
|
||||
"client_configuration",
|
||||
"sod_opcodes",
|
||||
"login_opcodes.conf"
|
||||
)
|
||||
path.GetOpcodePath(),
|
||||
"login_opcodes_sod.conf"
|
||||
);
|
||||
|
||||
CheckSoDOpcodeFile(opcodes_path);
|
||||
|
||||
if (!sod_ops->LoadOpcodes(opcodes_path.c_str())) {
|
||||
LogError(
|
||||
"ClientManager fatal error: couldn't load opcodes for SoD file {0}",
|
||||
@@ -88,6 +157,44 @@ ClientManager::ClientManager()
|
||||
clients.push_back(c);
|
||||
}
|
||||
);
|
||||
|
||||
int larion_port = server.config.GetVariableInt("client_configuration", "larion_port", 15900);
|
||||
|
||||
EQStreamManagerInterfaceOptions larion_opts(larion_port, false, false);
|
||||
|
||||
larion_stream = new EQ::Net::EQStreamManager(larion_opts);
|
||||
larion_ops = new RegularOpcodeManager;
|
||||
|
||||
opcodes_path = fmt::format(
|
||||
"{}/{}",
|
||||
path.GetOpcodePath(),
|
||||
"login_opcodes_larion.conf"
|
||||
);
|
||||
|
||||
CheckLarionOpcodeFile(opcodes_path);
|
||||
|
||||
if (!larion_ops->LoadOpcodes(opcodes_path.c_str())) {
|
||||
LogError(
|
||||
"ClientManager fatal error: couldn't load opcodes for Larion file [{0}]",
|
||||
server.config.GetVariableString("client_configuration", "larion_opcodes", "login_opcodes.conf")
|
||||
);
|
||||
|
||||
run_server = false;
|
||||
}
|
||||
|
||||
larion_stream->OnNewConnection(
|
||||
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
|
||||
LogInfo(
|
||||
"New Larion client connection from [{0}:{1}]",
|
||||
long2ip(stream->GetRemoteIP()),
|
||||
stream->GetRemotePort()
|
||||
);
|
||||
|
||||
stream->SetOpcodeManager(&larion_ops);
|
||||
Client* c = new Client(stream, cv_larion);
|
||||
clients.push_back(c);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ClientManager::~ClientManager()
|
||||
|
||||
@@ -55,6 +55,8 @@ private:
|
||||
EQ::Net::EQStreamManager *titanium_stream;
|
||||
OpcodeManager *sod_ops;
|
||||
EQ::Net::EQStreamManager *sod_stream;
|
||||
OpcodeManager *larion_ops;
|
||||
EQ::Net::EQStreamManager* larion_stream;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -83,7 +83,8 @@ struct PlayEverquestResponse_Struct {
|
||||
|
||||
enum LSClientVersion {
|
||||
cv_titanium,
|
||||
cv_sod
|
||||
cv_sod,
|
||||
cv_larion
|
||||
};
|
||||
|
||||
enum LSClientStatus {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#EQEmu Public Login Server OPCodes
|
||||
OP_SessionReady=0x0001
|
||||
OP_Login=0x0002
|
||||
OP_ServerListRequest=0x0004
|
||||
OP_PlayEverquestRequest=0x000d
|
||||
OP_PlayEverquestResponse=0x0022
|
||||
OP_ChatMessage=0x0017
|
||||
OP_LoginAccepted=0x0018
|
||||
OP_ServerListResponse=0x0019
|
||||
OP_Poll=0x0029
|
||||
OP_EnterChat=0x000f
|
||||
OP_PollResponse=0x0011
|
||||
OP_SystemFingerprint=0x0016
|
||||
OP_ExpansionList=0x0030
|
||||
@@ -137,7 +137,7 @@ std::unique_ptr<EQApplicationPacket> ServerManager::CreateServerListPacket(Clien
|
||||
use_local_ip ? "Local" : "Remote"
|
||||
);
|
||||
|
||||
world_server->SerializeForClientServerList(buf, use_local_ip);
|
||||
world_server->SerializeForClientServerList(buf, use_local_ip, client->GetClientVersion());
|
||||
}
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf);
|
||||
|
||||
@@ -977,7 +977,7 @@ bool WorldServer::ValidateWorldServerAdminLogin(
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const
|
||||
void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const
|
||||
{
|
||||
// see LoginClientServerData_Struct
|
||||
if (use_local_ip) {
|
||||
@@ -987,19 +987,31 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_lo
|
||||
out.WriteString(GetRemoteIP());
|
||||
}
|
||||
|
||||
switch (GetServerListID()) {
|
||||
case LS::ServerType::Legends:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Legends);
|
||||
break;
|
||||
case LS::ServerType::Preferred:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Preferred);
|
||||
break;
|
||||
default:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Standard);
|
||||
break;
|
||||
if (version == cv_larion) {
|
||||
out.WriteUInt32(9000);
|
||||
}
|
||||
|
||||
switch (GetServerListID()) {
|
||||
case LS::ServerType::Legends:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Legends);
|
||||
break;
|
||||
case LS::ServerType::Preferred:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Preferred);
|
||||
break;
|
||||
default:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Standard);
|
||||
break;
|
||||
}
|
||||
if (version == cv_larion) {
|
||||
auto server_id = GetServerId();
|
||||
//if this is 0, the client will not show the server in the list
|
||||
out.WriteUInt32(1);
|
||||
out.WriteUInt32(server_id);
|
||||
}
|
||||
else {
|
||||
out.WriteUInt32(GetServerId());
|
||||
}
|
||||
|
||||
out.WriteUInt32(GetServerId());
|
||||
out.WriteString(GetServerLongName());
|
||||
out.WriteString("us"); // country code
|
||||
out.WriteString("en"); // language code
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../common/packet_dump.h"
|
||||
#include "database.h"
|
||||
#include "../common/event/timer.h"
|
||||
#include "login_types.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
@@ -149,7 +150,7 @@ public:
|
||||
bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration);
|
||||
bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration);
|
||||
|
||||
void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip) const;
|
||||
void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.52.0",
|
||||
"version": "22.59.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -433,6 +433,7 @@ OP_TraderShop=0x31df
|
||||
OP_TraderBulkSend=0x6a96
|
||||
OP_Trader=0x4ef5
|
||||
OP_Barter=0x243a
|
||||
OP_BuyerItems=0x1a6a
|
||||
OP_TraderBuy=0x0000
|
||||
OP_ShopItem=0x0000
|
||||
OP_BazaarInspect=0x0000
|
||||
|
||||
@@ -142,7 +142,6 @@ foreach my $table_to_generate (@tables) {
|
||||
"guild_bank",
|
||||
"inventory_versions",
|
||||
"raid_leaders",
|
||||
"sharedbank",
|
||||
"trader_audit",
|
||||
"eqtime",
|
||||
"db_version",
|
||||
|
||||
@@ -287,6 +287,7 @@ void Adventure::Finished(AdventureWinStatus ws)
|
||||
auto character_id = database.GetCharacterID(*iter);
|
||||
|
||||
if (character_id == 0) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+68
-14
@@ -526,9 +526,27 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||
SendEnterWorld(cle->name());
|
||||
SendPostEnterWorld();
|
||||
if (!is_player_zoning) {
|
||||
SendExpansionInfo();
|
||||
SendCharInfo();
|
||||
database.LoginIP(cle->AccountID(), long2ip(GetIP()));
|
||||
const auto supported_clients = RuleS(World, SupportedClients);
|
||||
bool skip_char_info = false;
|
||||
if (!supported_clients.empty()) {
|
||||
const std::string& name = EQ::versions::ClientVersionName(m_ClientVersion);
|
||||
const auto& clients = Strings::Split(supported_clients, ",");
|
||||
if (std::find(clients.begin(), clients.end(), name) == clients.end()) {
|
||||
SendUnsupportedClientPacket(
|
||||
fmt::format(
|
||||
"Client Not In Supported List [{}]",
|
||||
supported_clients
|
||||
)
|
||||
);
|
||||
skip_char_info = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip_char_info) {
|
||||
SendExpansionInfo();
|
||||
SendCharInfo();
|
||||
database.LoginIP(cle->AccountID(), long2ip(GetIP()));
|
||||
}
|
||||
}
|
||||
|
||||
cle->SetIP(GetIP());
|
||||
@@ -2351,21 +2369,26 @@ bool Client::StoreCharacter(
|
||||
|
||||
auto e = InventoryRepository::NewEntity();
|
||||
|
||||
e.charid = character_id;
|
||||
e.character_id = character_id;
|
||||
|
||||
for (int16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invbag::BANK_BAGS_END;) {
|
||||
const auto inst = p_inventory_profile->GetItem(slot_id);
|
||||
if (inst) {
|
||||
e.slotid = slot_id;
|
||||
e.itemid = inst->GetItem()->ID;
|
||||
e.charges = inst->GetCharges();
|
||||
e.color = inst->GetColor();
|
||||
e.augslot1 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN);
|
||||
e.augslot2 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1);
|
||||
e.augslot3 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2);
|
||||
e.augslot4 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3);
|
||||
e.augslot5 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4);
|
||||
e.augslot6 = inst->GetAugmentItemID(EQ::invaug::SOCKET_END);
|
||||
e.slot_id = slot_id;
|
||||
e.item_id = inst->GetItem()->ID;
|
||||
e.charges = inst->GetCharges();
|
||||
e.color = inst->GetColor();
|
||||
e.augment_one = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN);
|
||||
e.augment_two = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1);
|
||||
e.augment_three = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2);
|
||||
e.augment_four = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3);
|
||||
e.augment_five = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4);
|
||||
e.augment_six = inst->GetAugmentItemID(EQ::invaug::SOCKET_END);
|
||||
e.instnodrop = inst->IsAttuned() ? 1 : 0;
|
||||
e.ornament_icon = inst->GetOrnamentationIcon();
|
||||
e.ornament_idfile = inst->GetOrnamentationIDFile();
|
||||
e.ornament_hero_model = inst->GetOrnamentHeroModel();
|
||||
e.guid = inst->GetSerialNumber();
|
||||
|
||||
v.emplace_back(e);
|
||||
}
|
||||
@@ -2453,3 +2476,34 @@ void Client::SendGuildTributeOptInToggle(const GuildTributeMemberToggle *in)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::SendUnsupportedClientPacket(const std::string& message)
|
||||
{
|
||||
EQApplicationPacket packet(OP_SendCharInfo, sizeof(CharacterSelect_Struct) + sizeof(CharacterSelectEntry_Struct));
|
||||
|
||||
unsigned char* buff_ptr = packet.pBuffer;
|
||||
auto cs = (CharacterSelect_Struct*) buff_ptr;
|
||||
|
||||
cs->CharCount = 1;
|
||||
cs->TotalChars = 1;
|
||||
|
||||
buff_ptr += sizeof(CharacterSelect_Struct);
|
||||
|
||||
auto e = (CharacterSelectEntry_Struct*) buff_ptr;
|
||||
|
||||
strcpy(e->Name, message.c_str());
|
||||
|
||||
e->Race = Race::Human;
|
||||
e->Class = Class::Warrior;
|
||||
e->Level = 1;
|
||||
e->ShroudClass = e->Class;
|
||||
e->ShroudRace = e->Race;
|
||||
e->Zone = std::numeric_limits<uint16>::max();
|
||||
e->Instance = 0;
|
||||
e->Gender = Gender::Male;
|
||||
e->GoHome = 0;
|
||||
e->Tutorial = 0;
|
||||
e->Enabled = 0;
|
||||
|
||||
QueuePacket(&packet);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ private:
|
||||
EQStreamInterface* eqs;
|
||||
bool CanTradeFVNoDropItem();
|
||||
void RecordPossibleHack(const std::string& message);
|
||||
void SendUnsupportedClientPacket(const std::string& message);
|
||||
};
|
||||
|
||||
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc);
|
||||
|
||||
+279
-234
@@ -528,276 +528,321 @@ void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
|
||||
void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_Struct* whom, WorldTCPConnection* connection) {
|
||||
try{
|
||||
LinkedListIterator<ClientListEntry*> iterator(clientlist);
|
||||
LinkedListIterator<ClientListEntry*> countclients(clientlist);
|
||||
ClientListEntry* cle = 0;
|
||||
ClientListEntry* countcle = 0;
|
||||
//char tmpgm[25] = "";
|
||||
//char accinfo[150] = "";
|
||||
char line[300] = "";
|
||||
//char tmpguild[50] = "";
|
||||
//char LFG[10] = "";
|
||||
//uint32 x = 0;
|
||||
int whomlen = 0;
|
||||
if (whom) {
|
||||
// fixes for client converting some queries into a race query instead of zone
|
||||
if (whom->wrace == 221) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "scarlet");
|
||||
}
|
||||
if (whom->wrace == 327) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "crystal");
|
||||
}
|
||||
if (whom->wrace == 103) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "kedge");
|
||||
}
|
||||
if (whom->wrace == 230) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "akheva");
|
||||
}
|
||||
if (whom->wrace == 229) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "netherbian");
|
||||
}
|
||||
try {
|
||||
LinkedListIterator<ClientListEntry*> iterator(clientlist);
|
||||
LinkedListIterator<ClientListEntry*> countclients(clientlist);
|
||||
ClientListEntry* cle = 0;
|
||||
ClientListEntry* countcle = 0;
|
||||
//char tmpgm[25] = "";
|
||||
//char accinfo[150] = "";
|
||||
char line[300] = "";
|
||||
//char tmpguild[50] = "";
|
||||
//char LFG[10] = "";
|
||||
//uint32 x = 0;
|
||||
int whomlen = 0;
|
||||
|
||||
whomlen = strlen(whom->whom);
|
||||
if(whom->wrace == 0x001A) // 0x001A is the old Froglok race number and is sent by the client for /who all froglok
|
||||
whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number.
|
||||
}
|
||||
|
||||
uint32 totalusers=0;
|
||||
uint32 totallength=0;
|
||||
countclients.Reset();
|
||||
while(countclients.MoreElements()){
|
||||
countcle = countclients.GetData();
|
||||
const char* tmpZone = ZoneName(countcle->zone());
|
||||
if (
|
||||
(countcle->Online() >= CLE_Status::Zoning) &&
|
||||
(!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((countcle->Admin() >= AccountStatus::QuestTroupe && countcle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF || (countcle->level() >= whom->lvllow && countcle->level() <= whom->lvlhigh && (countcle->Anon()==0 || admin > countcle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (countcle->class_() == whom->wclass && (countcle->Anon()==0 || admin > countcle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF || (countcle->race() == whom->wrace && (countcle->Anon()==0 || admin > countcle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(countcle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
if((countcle->Anon()>0 && admin >= countcle->Admin() && admin > AccountStatus::Player) || countcle->Anon()==0 ){
|
||||
totalusers++;
|
||||
if(totalusers<=20 || admin >= AccountStatus::GMAdmin)
|
||||
totallength=totallength+strlen(countcle->name())+strlen(countcle->AccountName())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5;
|
||||
if (whom) {
|
||||
// fixes for client converting some queries into a race query instead of zone
|
||||
if (whom->wrace == 221) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "scarlet");
|
||||
}
|
||||
else if((countcle->Anon()>0 && admin<=countcle->Admin()) || (countcle->Anon()==0 && !countcle->GetGM())) {
|
||||
totalusers++;
|
||||
if(totalusers<=20 || admin >= AccountStatus::GMAdmin)
|
||||
totallength=totallength+strlen(countcle->name())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5;
|
||||
|
||||
if (whom->wrace == 327) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "crystal");
|
||||
}
|
||||
|
||||
if (whom->wrace == 103) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "kedge");
|
||||
}
|
||||
|
||||
if (whom->wrace == 230) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "akheva");
|
||||
}
|
||||
|
||||
if (whom->wrace == 229) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "netherbian");
|
||||
}
|
||||
|
||||
whomlen = strlen(whom->whom);
|
||||
|
||||
if (whom->wrace == 0x001A) { // 0x001A is the old Froglok race number and is sent by the client for /who all froglok
|
||||
whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number.
|
||||
}
|
||||
}
|
||||
countclients.Advance();
|
||||
}
|
||||
uint32 plid=fromid;
|
||||
uint32 playerineqstring=5001;
|
||||
const char line2[]="---------------------------";
|
||||
uint8 unknown35=0x0A;
|
||||
uint32 unknown36=0;
|
||||
uint32 playersinzonestring=5028;
|
||||
if(totalusers>20 && admin<AccountStatus::GMAdmin){
|
||||
totalusers=20;
|
||||
playersinzonestring=5033;
|
||||
}
|
||||
else if(totalusers>1)
|
||||
playersinzonestring=5036;
|
||||
uint32 unknown44[2];
|
||||
unknown44[0]=0;
|
||||
unknown44[1]=0;
|
||||
uint32 unknown52=totalusers;
|
||||
uint32 unknown56=1;
|
||||
auto pack2 = new ServerPacket(ServerOP_WhoAllReply, 64 + totallength + (49 * totalusers));
|
||||
memset(pack2->pBuffer,0,pack2->size);
|
||||
uchar *buffer=pack2->pBuffer;
|
||||
uchar *bufptr=buffer;
|
||||
//memset(buffer,0,pack2->size);
|
||||
memcpy(bufptr,&plid, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playerineqstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&line2, strlen(line2));
|
||||
bufptr+=strlen(line2);
|
||||
memcpy(bufptr,&unknown35, sizeof(uint8));
|
||||
bufptr+=sizeof(uint8);
|
||||
memcpy(bufptr,&unknown36, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playersinzonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown52, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown56, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&totalusers, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
|
||||
iterator.Reset();
|
||||
int idx=-1;
|
||||
while(iterator.MoreElements()) {
|
||||
cle = iterator.GetData();
|
||||
const char* tmpZone = ZoneName(cle->zone());
|
||||
uint32 totalusers=0;
|
||||
uint32 totallength=0;
|
||||
countclients.Reset();
|
||||
while (countclients.MoreElements()) {
|
||||
countcle = countclients.GetData();
|
||||
const char* tmpZone = ZoneName(countcle->zone());
|
||||
if (
|
||||
(countcle->Online() >= CLE_Status::Zoning) &&
|
||||
(!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((countcle->Admin() >= AccountStatus::QuestTroupe && countcle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF ||
|
||||
(countcle->level() >= whom->lvllow && countcle->level() <= whom->lvlhigh &&
|
||||
(countcle->Anon() == 0 || admin > countcle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (countcle->class_() == whom->wclass &&
|
||||
(countcle->Anon() == 0 || admin > countcle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF ||
|
||||
(countcle->race() == whom->wrace && (countcle->Anon() == 0 || admin > countcle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(countcle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
// these blocks can all be condensed but it's simpler to conceptualize this way
|
||||
if ((countcle->Anon()>0 && admin >= countcle->Admin() && admin > AccountStatus::Player) || countcle->Anon()==0 ) {
|
||||
totalusers++;
|
||||
if (totalusers<=20 || admin >= AccountStatus::GMAdmin) {
|
||||
totallength = totallength + strlen(countcle->name()) + strlen(countcle->AccountName()) +
|
||||
strlen(guild_mgr.GetGuildName(countcle->GuildID())) + 5;
|
||||
}
|
||||
} else if (((countcle->Anon() == 1 && admin <= countcle->Admin()) && whomlen != 0 &&
|
||||
strncasecmp(countcle->name(), whom->whom, whomlen) == 0)) {
|
||||
totalusers++;
|
||||
if (totalusers <= 20 || admin >= AccountStatus::GMAdmin) {
|
||||
totallength = totallength + strlen(countcle->name()) + strlen(countcle->AccountName()) +
|
||||
strlen(guild_mgr.GetGuildName(countcle->GuildID())) + 5;
|
||||
}
|
||||
} else if (((countcle->Anon() == 2 && admin <= countcle->Admin()) && whomlen != 0 &&
|
||||
(strncasecmp(countcle->name(), whom->whom, whomlen) == 0 ||
|
||||
strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0))) {
|
||||
totalusers++;
|
||||
if (totalusers <= 20 || admin >= AccountStatus::GMAdmin) {
|
||||
totallength = totallength + strlen(countcle->name()) + strlen(countcle->AccountName()) +
|
||||
strlen(guild_mgr.GetGuildName(countcle->GuildID())) + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
countclients.Advance();
|
||||
}
|
||||
|
||||
if (
|
||||
(cle->Online() >= CLE_Status::Zoning) &&
|
||||
(!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (cle->class_() == whom->wclass && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF || (cle->race() == whom->wrace && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(cle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
line[0] = 0;
|
||||
uint32 rankstring = 0xFFFFFFFF;
|
||||
if((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin < AccountStatus::GMAdmin)){ //hide gms that are anon from lesser gms and normal players, cut off at 20
|
||||
uint32 plid=fromid;
|
||||
uint32 playerineqstring=5001;
|
||||
const char line2[]="---------------------------";
|
||||
uint8 unknown35=0x0A;
|
||||
uint32 unknown36=0;
|
||||
uint32 playersinzonestring=5028;
|
||||
|
||||
if (totalusers>20 && admin<AccountStatus::GMAdmin) {
|
||||
totalusers=20;
|
||||
playersinzonestring=5033;
|
||||
} else if(totalusers>1) {
|
||||
playersinzonestring=5036;
|
||||
}
|
||||
|
||||
uint32 unknown44[2];
|
||||
unknown44[0]=0;
|
||||
unknown44[1]=0;
|
||||
uint32 unknown52=totalusers;
|
||||
uint32 unknown56=1;
|
||||
auto pack2 = new ServerPacket(ServerOP_WhoAllReply, 64 + totallength + (49 * totalusers));
|
||||
memset(pack2->pBuffer,0,pack2->size);
|
||||
uchar *buffer=pack2->pBuffer;
|
||||
uchar *bufptr=buffer;
|
||||
//memset(buffer,0,pack2->size);
|
||||
memcpy(bufptr,&plid, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playerineqstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&line2, strlen(line2));
|
||||
bufptr+=strlen(line2);
|
||||
memcpy(bufptr,&unknown35, sizeof(uint8));
|
||||
bufptr+=sizeof(uint8);
|
||||
memcpy(bufptr,&unknown36, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playersinzonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown52, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown56, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&totalusers, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
|
||||
iterator.Reset();
|
||||
int idx=-1;
|
||||
while(iterator.MoreElements()) {
|
||||
cle = iterator.GetData();
|
||||
const char* tmpZone = ZoneName(cle->zone());
|
||||
|
||||
if (
|
||||
(cle->Online() >= CLE_Status::Zoning) &&
|
||||
(!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (cle->class_() == whom->wclass && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF || (cle->race() == whom->wrace && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(cle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
line[0] = 0;
|
||||
uint32 rankstring = 0xFFFFFFFF;
|
||||
// These lines can be simplified but easier to conceptualize this way
|
||||
if ((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin < AccountStatus::GMAdmin)) { //hide gms that are anon from lesser gms and normal players, cut off at 20
|
||||
rankstring = 0;
|
||||
iterator.Advance();
|
||||
continue;
|
||||
} else if (cle->Anon() == 1 && cle->Admin()>=admin && (whomlen == 0 || (whomlen !=0 && strncasecmp(cle->name(), whom->whom, whomlen) != 0))) {
|
||||
rankstring = 0;
|
||||
iterator.Advance();
|
||||
continue;
|
||||
} else if (cle->Anon() == 2 && cle->Admin()>=admin && (whomlen == 0 || (whomlen !=0 && strncasecmp(cle->name(), whom->whom, whomlen) != 0 && strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) != 0))) {
|
||||
rankstring = 0;
|
||||
iterator.Advance();
|
||||
continue;
|
||||
} else if (cle->GetGM()) {
|
||||
if (cle->Admin() >= AccountStatus::GMImpossible)
|
||||
if (cle->Admin() >= AccountStatus::GMImpossible) {
|
||||
rankstring = 5021;
|
||||
else if (cle->Admin() >= AccountStatus::GMMgmt)
|
||||
} else if (cle->Admin() >= AccountStatus::GMMgmt) {
|
||||
rankstring = 5020;
|
||||
else if (cle->Admin() >= AccountStatus::GMCoder)
|
||||
} else if (cle->Admin() >= AccountStatus::GMCoder) {
|
||||
rankstring = 5019;
|
||||
else if (cle->Admin() >= AccountStatus::GMAreas)
|
||||
} else if (cle->Admin() >= AccountStatus::GMAreas) {
|
||||
rankstring = 5018;
|
||||
else if (cle->Admin() >= AccountStatus::QuestMaster)
|
||||
} else if (cle->Admin() >= AccountStatus::QuestMaster) {
|
||||
rankstring = 5017;
|
||||
else if (cle->Admin() >= AccountStatus::GMLeadAdmin)
|
||||
} else if (cle->Admin() >= AccountStatus::GMLeadAdmin) {
|
||||
rankstring = 5016;
|
||||
else if (cle->Admin() >= AccountStatus::GMAdmin)
|
||||
} else if (cle->Admin() >= AccountStatus::GMAdmin) {
|
||||
rankstring = 5015;
|
||||
else if (cle->Admin() >= AccountStatus::GMStaff)
|
||||
} else if (cle->Admin() >= AccountStatus::GMStaff) {
|
||||
rankstring = 5014;
|
||||
else if (cle->Admin() >= AccountStatus::EQSupport)
|
||||
} else if (cle->Admin() >= AccountStatus::EQSupport) {
|
||||
rankstring = 5013;
|
||||
else if (cle->Admin() >= AccountStatus::GMTester)
|
||||
} else if (cle->Admin() >= AccountStatus::GMTester) {
|
||||
rankstring = 5012;
|
||||
else if (cle->Admin() >= AccountStatus::SeniorGuide)
|
||||
} else if (cle->Admin() >= AccountStatus::SeniorGuide) {
|
||||
rankstring = 5011;
|
||||
else if (cle->Admin() >= AccountStatus::QuestTroupe)
|
||||
} else if (cle->Admin() >= AccountStatus::QuestTroupe) {
|
||||
rankstring = 5010;
|
||||
else if (cle->Admin() >= AccountStatus::Guide)
|
||||
} else if (cle->Admin() >= AccountStatus::Guide) {
|
||||
rankstring = 5009;
|
||||
else if (cle->Admin() >= AccountStatus::ApprenticeGuide)
|
||||
} else if (cle->Admin() >= AccountStatus::ApprenticeGuide) {
|
||||
rankstring = 5008;
|
||||
else if (cle->Admin() >= AccountStatus::Steward)
|
||||
} else if (cle->Admin() >= AccountStatus::Steward) {
|
||||
rankstring = 5007;
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
char guildbuffer[67]={0};
|
||||
if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0)
|
||||
sprintf(guildbuffer,"<%s>", guild_mgr.GetGuildName(cle->GuildID()));
|
||||
uint32 formatstring=5025;
|
||||
if(cle->Anon()==1 && (admin<cle->Admin() || admin == AccountStatus::Player))
|
||||
formatstring=5024;
|
||||
else if(cle->Anon()==1 && admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
formatstring=5022;
|
||||
else if(cle->Anon()==2 && (admin<cle->Admin() || admin == AccountStatus::Player))
|
||||
formatstring=5023;//display guild
|
||||
else if(cle->Anon()==2 && admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
formatstring=5022;//display everything
|
||||
|
||||
//war* wars2 = (war*)pack2->pBuffer;
|
||||
idx++;
|
||||
char guildbuffer[67]={0};
|
||||
|
||||
uint32 plclass_=0;
|
||||
uint32 pllevel=0;
|
||||
uint32 pidstring=0xFFFFFFFF;//5003;
|
||||
uint32 plrace=0;
|
||||
uint32 zonestring=0xFFFFFFFF;
|
||||
uint32 plzone=0;
|
||||
uint32 unknown80[2];
|
||||
if(cle->Anon()==0 || (admin>=cle->Admin() && admin> AccountStatus::Player)){
|
||||
plclass_=cle->class_();
|
||||
pllevel=cle->level();
|
||||
if(admin>=AccountStatus::GMAdmin)
|
||||
pidstring=5003;
|
||||
plrace=cle->race();
|
||||
zonestring=5006;
|
||||
plzone=cle->zone();
|
||||
}
|
||||
if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0) {
|
||||
sprintf(guildbuffer,"<%s>", guild_mgr.GetGuildName(cle->GuildID()));
|
||||
}
|
||||
|
||||
uint32 formatstring=5025;
|
||||
|
||||
if(admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
unknown80[0]=cle->Admin();
|
||||
else
|
||||
unknown80[0]=0xFFFFFFFF;
|
||||
unknown80[1]=0xFFFFFFFF;//1035
|
||||
if (cle->Anon()==1 && (admin<cle->Admin() || admin == AccountStatus::Player)) {
|
||||
formatstring=5024;
|
||||
} else if(cle->Anon()==1 && admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
formatstring=5022;
|
||||
} else if(cle->Anon()==2 && (admin<cle->Admin() || admin == AccountStatus::Player)) {
|
||||
formatstring=5023;//display guild
|
||||
} else if(cle->Anon()==2 && admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
formatstring=5022;//display everything
|
||||
}
|
||||
|
||||
//war* wars2 = (war*)pack2->pBuffer;
|
||||
|
||||
//char plstatus[20]={0};
|
||||
//sprintf(plstatus, "Status %i",cle->Admin());
|
||||
char plname[64]={0};
|
||||
strcpy(plname,cle->name());
|
||||
uint32 plclass_=0;
|
||||
uint32 pllevel=0;
|
||||
uint32 pidstring=0xFFFFFFFF;//5003;
|
||||
uint32 plrace=0;
|
||||
uint32 zonestring=0xFFFFFFFF;
|
||||
uint32 plzone=0;
|
||||
uint32 unknown80[2];
|
||||
|
||||
char placcount[30]={0};
|
||||
if(admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
strcpy(placcount,cle->AccountName());
|
||||
if (cle->Anon()==0 || (admin>=cle->Admin() && admin> AccountStatus::Player)) {
|
||||
plclass_=cle->class_();
|
||||
pllevel=cle->level();
|
||||
|
||||
memcpy(bufptr,&formatstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pidstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plname, strlen(plname)+1);
|
||||
bufptr+=strlen(plname)+1;
|
||||
memcpy(bufptr,&rankstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&guildbuffer, strlen(guildbuffer)+1);
|
||||
bufptr+=strlen(guildbuffer)+1;
|
||||
memcpy(bufptr,&unknown80[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown80[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&zonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plzone, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plclass_, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pllevel, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plrace, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
uint32 ending=0;
|
||||
memcpy(bufptr,&placcount, strlen(placcount)+1);
|
||||
bufptr+=strlen(placcount)+1;
|
||||
ending=207;
|
||||
memcpy(bufptr,&ending, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
if(admin>=AccountStatus::GMAdmin) {
|
||||
pidstring=5003;
|
||||
}
|
||||
plrace=cle->race();
|
||||
zonestring=5006;
|
||||
plzone=cle->zone();
|
||||
}
|
||||
|
||||
if (admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
unknown80[0]=cle->Admin();
|
||||
} else {
|
||||
unknown80[0]=0xFFFFFFFF;
|
||||
}
|
||||
|
||||
unknown80[1]=0xFFFFFFFF;//1035
|
||||
|
||||
//char plstatus[20]={0};
|
||||
//sprintf(plstatus, "Status %i",cle->Admin());
|
||||
char plname[64]={0};
|
||||
strcpy(plname,cle->name());
|
||||
|
||||
char placcount[30]={0};
|
||||
if (admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
strcpy(placcount,cle->AccountName());
|
||||
}
|
||||
|
||||
memcpy(bufptr,&formatstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pidstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plname, strlen(plname)+1);
|
||||
bufptr+=strlen(plname)+1;
|
||||
memcpy(bufptr,&rankstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&guildbuffer, strlen(guildbuffer)+1);
|
||||
bufptr+=strlen(guildbuffer)+1;
|
||||
memcpy(bufptr,&unknown80[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown80[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&zonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plzone, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plclass_, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pllevel, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plrace, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
uint32 ending=0;
|
||||
memcpy(bufptr,&placcount, strlen(placcount)+1);
|
||||
bufptr+=strlen(placcount)+1;
|
||||
ending=207;
|
||||
memcpy(bufptr,&ending, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
//zoneserver_list.SendPacket(pack2); // NO NO NO WHY WOULD YOU SEND IT TO EVERY ZONE SERVER?!?
|
||||
SendPacket(to,pack2);
|
||||
safe_delete(pack2);
|
||||
}
|
||||
catch(...){
|
||||
|
||||
SendPacket(to,pack2);
|
||||
safe_delete(pack2);
|
||||
} catch(...) {
|
||||
LogInfo("Unknown error in world's SendWhoAll (probably mem error), ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
+26
-8
@@ -315,6 +315,13 @@ void LoginServer::ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p)
|
||||
error,
|
||||
reason
|
||||
);
|
||||
|
||||
if (m_legacy_client) {
|
||||
m_legacy_client.release();
|
||||
}
|
||||
else if (m_client) {
|
||||
m_client.release();
|
||||
}
|
||||
}
|
||||
|
||||
void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p)
|
||||
@@ -598,6 +605,11 @@ bool LoginServer::Connect()
|
||||
|
||||
void LoginServer::SendInfo()
|
||||
{
|
||||
if (m_client == nullptr && m_legacy_client == nullptr) {
|
||||
LogDebug("No client to send info to loginserver");
|
||||
return;
|
||||
}
|
||||
|
||||
const WorldConfig *Config = WorldConfig::get();
|
||||
|
||||
auto pack = new ServerPacket;
|
||||
@@ -643,6 +655,11 @@ void LoginServer::SendInfo()
|
||||
|
||||
void LoginServer::SendStatus()
|
||||
{
|
||||
if (m_client == nullptr && m_legacy_client == nullptr) {
|
||||
LogDebug("No client to send status to loginserver");
|
||||
return;
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket;
|
||||
pack->opcode = ServerOP_LSStatus;
|
||||
pack->size = sizeof(ServerLSStatus_Struct);
|
||||
@@ -671,20 +688,21 @@ void LoginServer::SendStatus()
|
||||
*/
|
||||
void LoginServer::SendPacket(ServerPacket *pack)
|
||||
{
|
||||
if (m_is_legacy) {
|
||||
if (m_legacy_client) {
|
||||
m_legacy_client->SendPacket(pack);
|
||||
}
|
||||
if (m_legacy_client) {
|
||||
m_legacy_client->SendPacket(pack);
|
||||
}
|
||||
else {
|
||||
if (m_client) {
|
||||
m_client->SendPacket(pack);
|
||||
}
|
||||
else if (m_client) {
|
||||
m_client->SendPacket(pack);
|
||||
}
|
||||
}
|
||||
|
||||
void LoginServer::SendAccountUpdate(ServerPacket *pack)
|
||||
{
|
||||
if (m_client == nullptr && m_legacy_client == nullptr) {
|
||||
LogDebug("No client to send account update to loginserver");
|
||||
return;
|
||||
}
|
||||
|
||||
auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer;
|
||||
if (CanUpdate()) {
|
||||
LogInfo(
|
||||
|
||||
@@ -289,6 +289,17 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
||||
LogInfo("Clearing inventory snapshots");
|
||||
database.ClearInvSnapshots();
|
||||
LogInfo("Loading items");
|
||||
LogInfo("Clearing trader table details");
|
||||
database.ClearTraderDetails();
|
||||
database.ClearBuyerDetails();
|
||||
LogInfo("Clearing buyer table details");
|
||||
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
LogInfo("Clearing [bot_pet_buffs] table of stale entries");
|
||||
database.QueryDatabase(
|
||||
"DELETE FROM bot_pet_buffs WHERE NOT EXISTS (SELECT * FROM bot_pets WHERE bot_pets.pets_index = bot_pet_buffs.pets_index)"
|
||||
);
|
||||
}
|
||||
|
||||
if (!content_db.LoadItems(hotfix_name)) {
|
||||
LogError("Error: Could not load item data. But ignoring");
|
||||
|
||||
+65
-89
@@ -26,6 +26,7 @@
|
||||
#include <vector>
|
||||
#include "sof_char_create_data.h"
|
||||
#include "../common/repositories/character_instance_safereturns_repository.h"
|
||||
#include "../common/repositories/inventory_repository.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
#include "../common/zone_store.h"
|
||||
|
||||
@@ -857,115 +858,90 @@ bool WorldDatabase::LoadCharacterCreateCombos()
|
||||
// this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only
|
||||
bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv)
|
||||
{
|
||||
if (!account_id || !name || !inv)
|
||||
if (!account_id || !name || !inv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string query = StringFormat(
|
||||
"SELECT"
|
||||
" slotid,"
|
||||
" itemid,"
|
||||
" charges,"
|
||||
" color,"
|
||||
" augslot1,"
|
||||
" augslot2,"
|
||||
" augslot3,"
|
||||
" augslot4,"
|
||||
" augslot5,"
|
||||
" augslot6,"
|
||||
" instnodrop,"
|
||||
" custom_data,"
|
||||
" ornamenticon,"
|
||||
" ornamentidfile,"
|
||||
" ornament_hero_model "
|
||||
"FROM"
|
||||
" inventory "
|
||||
"INNER JOIN"
|
||||
" character_data ch "
|
||||
"ON"
|
||||
" ch.id = charid "
|
||||
"WHERE"
|
||||
" ch.name = '%s' "
|
||||
"AND"
|
||||
" ch.account_id = %i "
|
||||
"AND"
|
||||
" slotid >= %i "
|
||||
"AND"
|
||||
" slotid <= %i",
|
||||
name,
|
||||
account_id,
|
||||
EQ::invslot::slotHead,
|
||||
EQ::invslot::slotFeet
|
||||
const uint32 character_id = GetCharacterID(name);
|
||||
|
||||
if (!character_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& l = InventoryRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`character_id` = {} AND `slot_id` BETWEEN {} AND {}",
|
||||
character_id,
|
||||
EQ::invslot::slotHead,
|
||||
EQ::invslot::slotFeet
|
||||
)
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
int16 slot_id = Strings::ToInt(row[0]);
|
||||
if (l.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (slot_id) {
|
||||
case EQ::invslot::slotFace:
|
||||
case EQ::invslot::slotEar2:
|
||||
case EQ::invslot::slotNeck:
|
||||
case EQ::invslot::slotShoulders:
|
||||
case EQ::invslot::slotBack:
|
||||
case EQ::invslot::slotFinger1:
|
||||
case EQ::invslot::slotFinger2:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
for (const auto& e : l) {
|
||||
switch (e.slot_id) {
|
||||
case EQ::invslot::slotFace:
|
||||
case EQ::invslot::slotEar2:
|
||||
case EQ::invslot::slotNeck:
|
||||
case EQ::invslot::slotShoulders:
|
||||
case EQ::invslot::slotBack:
|
||||
case EQ::invslot::slotFinger1:
|
||||
case EQ::invslot::slotFinger2:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 item_id = Strings::ToInt(row[1]);
|
||||
int8 charges = Strings::ToInt(row[2]);
|
||||
uint32 color = Strings::ToUnsignedInt(row[3]);
|
||||
|
||||
uint32 aug[EQ::invaug::SOCKET_COUNT];
|
||||
aug[0] = (uint32)Strings::ToInt(row[4]);
|
||||
aug[1] = (uint32)Strings::ToInt(row[5]);
|
||||
aug[2] = (uint32)Strings::ToInt(row[6]);
|
||||
aug[3] = (uint32)Strings::ToInt(row[7]);
|
||||
aug[4] = (uint32)Strings::ToInt(row[8]);
|
||||
aug[5] = (uint32)Strings::ToInt(row[9]);
|
||||
uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = {
|
||||
e.augment_one,
|
||||
e.augment_two,
|
||||
e.augment_three,
|
||||
e.augment_four,
|
||||
e.augment_five,
|
||||
e.augment_six
|
||||
};
|
||||
|
||||
bool instnodrop = ((row[10] && (uint16)Strings::ToInt(row[10])) ? true : false);
|
||||
uint32 ornament_icon = (uint32)Strings::ToUnsignedInt(row[12]);
|
||||
uint32 ornament_idfile = (uint32)Strings::ToUnsignedInt(row[13]);
|
||||
uint32 ornament_hero_model = (uint32)Strings::ToUnsignedInt(row[14]);
|
||||
|
||||
const EQ::ItemData *item = content_db.GetItem(item_id);
|
||||
if (!item)
|
||||
const EQ::ItemData* item = content_db.GetItem(e.item_id);
|
||||
if (!item) {
|
||||
continue;
|
||||
|
||||
EQ::ItemInstance *inst = content_db.CreateBaseItem(item, charges);
|
||||
|
||||
if (inst == nullptr)
|
||||
continue;
|
||||
|
||||
inst->SetAttuned(instnodrop);
|
||||
|
||||
if (row[11]) {
|
||||
std::string data_str(row[11]);
|
||||
inst->SetCustomDataString(data_str);
|
||||
}
|
||||
|
||||
inst->SetOrnamentIcon(ornament_icon);
|
||||
inst->SetOrnamentationIDFile(ornament_idfile);
|
||||
inst->SetOrnamentHeroModel(item->HerosForgeModel);
|
||||
EQ::ItemInstance *inst = content_db.CreateBaseItem(item, e.charges);
|
||||
|
||||
if (color > 0)
|
||||
inst->SetColor(color);
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inst->SetCharges(charges);
|
||||
inst->SetCharges(e.charges);
|
||||
|
||||
if (e.color > 0) {
|
||||
inst->SetColor(e.color);
|
||||
}
|
||||
|
||||
if (item->IsClassCommon()) {
|
||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||
if (aug[i])
|
||||
inst->PutAugment(this, i, aug[i]);
|
||||
if (augment_ids[i]) {
|
||||
inst->PutAugment(this, i, augment_ids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inv->PutItem(slot_id, *inst);
|
||||
inst->SetAttuned(e.instnodrop);
|
||||
|
||||
if (!e.custom_data.empty()) {
|
||||
inst->SetCustomDataString(e.custom_data);
|
||||
}
|
||||
|
||||
inst->SetOrnamentIcon(e.ornament_icon);
|
||||
inst->SetOrnamentationIDFile(e.ornament_idfile);
|
||||
inst->SetOrnamentHeroModel(e.ornament_hero_model);
|
||||
|
||||
inv->PutItem(e.slot_id, *inst);
|
||||
|
||||
safe_delete(inst);
|
||||
}
|
||||
|
||||
+44
-2
@@ -1742,7 +1742,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
}
|
||||
|
||||
zoneserver_list.SendPacketToBootedZones(pack);
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_BazaarPurchase: {
|
||||
@@ -1753,9 +1752,52 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
"ServerOP_BazaarPurchase",
|
||||
in->trader_buy_struct.trader_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
|
||||
auto trader = client_list.FindCLEByCharacterID(in->trader_buy_struct.trader_id);
|
||||
if (trader) {
|
||||
zoneserver_list.SendPacket(trader->zone(), trader->instance(), pack);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_BuyerMessaging: {
|
||||
auto in = (BuyerMessaging_Struct *)pack->pBuffer;
|
||||
switch (in->action) {
|
||||
case Barter_AddToBarterWindow:
|
||||
case Barter_RemoveFromBarterWindow: {
|
||||
if (in->buyer_id <= 0) {
|
||||
LogTrading("World Message <red>[{}] received with invalid buyer_id <red>[{}]",
|
||||
"ServerOP_BecomeBuyer",
|
||||
in->buyer_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
zoneserver_list.SendPacketToBootedZones(pack);
|
||||
break;
|
||||
}
|
||||
case Barter_SellItem: {
|
||||
auto buyer = client_list.FindCharacter(in->buyer_name);
|
||||
if (buyer) {
|
||||
zoneserver_list.SendPacket(buyer->zone(), buyer->instance(), pack);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Barter_FailedTransaction:
|
||||
case Barter_BuyerTransactionComplete: {
|
||||
auto seller = client_list.FindCharacter(in->seller_name);
|
||||
if (seller) {
|
||||
zoneserver_list.SendPacket(seller->zone(), seller->instance(), pack);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
||||
|
||||
@@ -54,6 +54,7 @@ SET(zone_sources
|
||||
lua_buff.cpp
|
||||
lua_corpse.cpp
|
||||
lua_client.cpp
|
||||
lua_database.cpp
|
||||
lua_door.cpp
|
||||
lua_encounter.cpp
|
||||
lua_entity.cpp
|
||||
@@ -65,6 +66,7 @@ SET(zone_sources
|
||||
lua_inventory.cpp
|
||||
lua_item.cpp
|
||||
lua_iteminst.cpp
|
||||
lua_merc.cpp
|
||||
lua_mob.cpp
|
||||
lua_mod.cpp
|
||||
lua_npc.cpp
|
||||
@@ -109,12 +111,14 @@ SET(zone_sources
|
||||
perl_bot.cpp
|
||||
perl_buff.cpp
|
||||
perl_client.cpp
|
||||
perl_database.cpp
|
||||
perl_doors.cpp
|
||||
perl_entity.cpp
|
||||
perl_expedition.cpp
|
||||
perl_groups.cpp
|
||||
perl_hateentry.cpp
|
||||
perl_inventory.cpp
|
||||
perl_merc.cpp
|
||||
perl_mob.cpp
|
||||
perl_npc.cpp
|
||||
perl_object.cpp
|
||||
@@ -133,6 +137,7 @@ SET(zone_sources
|
||||
qglobals.cpp
|
||||
queryserv.cpp
|
||||
questmgr.cpp
|
||||
quest_db.cpp
|
||||
quest_parser_collection.cpp
|
||||
raids.cpp
|
||||
raycast_mesh.cpp
|
||||
@@ -213,6 +218,7 @@ SET(zone_headers
|
||||
lua_buff.h
|
||||
lua_client.h
|
||||
lua_corpse.h
|
||||
lua_database.h
|
||||
lua_door.h
|
||||
lua_encounter.h
|
||||
lua_entity.h
|
||||
@@ -224,6 +230,7 @@ SET(zone_headers
|
||||
lua_inventory.h
|
||||
lua_item.h
|
||||
lua_iteminst.h
|
||||
lua_merc.h
|
||||
lua_mob.h
|
||||
lua_mod.h
|
||||
lua_npc.h
|
||||
@@ -248,6 +255,7 @@ SET(zone_headers
|
||||
pathfinder_interface.h
|
||||
pathfinder_nav_mesh.h
|
||||
pathfinder_null.h
|
||||
perl_database.h
|
||||
perlpacket.h
|
||||
petitions.h
|
||||
pets.h
|
||||
@@ -257,6 +265,7 @@ SET(zone_headers
|
||||
queryserv.h
|
||||
quest_interface.h
|
||||
questmgr.h
|
||||
quest_db.h
|
||||
quest_parser_collection.h
|
||||
raids.h
|
||||
raycast_mesh.h
|
||||
|
||||
+5
-1
@@ -2185,7 +2185,7 @@ void Client::AutoGrantAAPoints() {
|
||||
SendAlternateAdvancementStats();
|
||||
}
|
||||
|
||||
void Client::GrantAllAAPoints(uint8 unlock_level)
|
||||
void Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
|
||||
{
|
||||
//iterate through every AA
|
||||
for (auto& aa : zone->aa_abilities) {
|
||||
@@ -2195,6 +2195,10 @@ void Client::GrantAllAAPoints(uint8 unlock_level)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ability->grant_only && skip_grant_only) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint8 level = unlock_level ? unlock_level : GetLevel();
|
||||
|
||||
AA::Rank* rank = ability->first;
|
||||
|
||||
+95
-7
@@ -222,7 +222,7 @@ void NPC::DescribeAggro(Client *to_who, Mob *mob, bool verbose) {
|
||||
if (
|
||||
GetLevel() < RuleI(Aggro, MinAggroLevel) &&
|
||||
mob->GetLevelCon(GetLevel()) == ConsiderColor::Gray &&
|
||||
GetBodyType() != BT_Undead &&
|
||||
GetBodyType() != BodyType::Undead &&
|
||||
!AlwaysAggro()
|
||||
) {
|
||||
to_who->Message(
|
||||
@@ -452,8 +452,26 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't aggro new clients if we are already engaged unless SpecialAbility::ProximityAggro is set
|
||||
if (IsEngaged() && (!GetSpecialAbility(SpecialAbility::ProximityAggro) || (GetSpecialAbility(SpecialAbility::ProximityAggro) && !CombatRange(mob)))) {
|
||||
// Don't aggro new clients if we are already engaged unless PROX_AGGRO is set
|
||||
// Frustrated mobs (all rooted up with no one to kill)
|
||||
// will engage without PROX_AGGRO ability if someone new is close now.
|
||||
|
||||
const bool is_frustrated = IsRooted() && !CombatRange(target);
|
||||
|
||||
if (
|
||||
!is_frustrated &&
|
||||
IsEngaged() &&
|
||||
(
|
||||
(
|
||||
!GetSpecialAbility(SpecialAbility::ProximityAggro) &&
|
||||
GetBodyType() != BodyType::Undead
|
||||
) ||
|
||||
(
|
||||
GetSpecialAbility(SpecialAbility::ProximityAggro) &&
|
||||
!CombatRange(mob)
|
||||
)
|
||||
)
|
||||
) {
|
||||
LogAggro(
|
||||
"[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]",
|
||||
GetName(),
|
||||
@@ -496,7 +514,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
RuleB(Aggro, UseLevelAggro) &&
|
||||
(
|
||||
GetLevel() >= RuleI(Aggro, MinAggroLevel) ||
|
||||
GetBodyType() == BT_Undead ||
|
||||
GetBodyType() == BodyType::Undead ||
|
||||
AlwaysAggro() ||
|
||||
(
|
||||
mob->IsClient() &&
|
||||
@@ -524,7 +542,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
} else {
|
||||
if (
|
||||
(
|
||||
(RuleB(Aggro, UndeadAlwaysAggro) && GetBodyType() == BT_Undead) ||
|
||||
(RuleB(Aggro, UndeadAlwaysAggro) && GetBodyType() == BodyType::Undead) ||
|
||||
(GetINT() <= RuleI(Aggro, IntAggroThreshold)) ||
|
||||
AlwaysAggro() ||
|
||||
(
|
||||
@@ -564,6 +582,76 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int EntityList::FleeAllyCount(Mob* attacker, Mob* skipped)
|
||||
{
|
||||
// Return a list of how many NPCs of the same faction or race are within aggro range of the given exclude Mob.
|
||||
if (!attacker) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (const auto& e : npc_list) {
|
||||
NPC* n = e.second;
|
||||
if (!n || n == skipped) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float aggro_range = n->GetAggroRange();
|
||||
const float assist_range = n->GetAssistRange();
|
||||
|
||||
if (assist_range > aggro_range) {
|
||||
aggro_range = assist_range;
|
||||
}
|
||||
|
||||
// Square it because we will be using DistNoRoot
|
||||
aggro_range *= aggro_range;
|
||||
|
||||
if (DistanceSquared(n->GetPosition(), skipped->GetPosition()) > aggro_range) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& excluded = Strings::Split(RuleS(Aggro, ExcludedFleeAllyFactionIDs));
|
||||
|
||||
const auto& f = std::find_if(
|
||||
excluded.begin(),
|
||||
excluded.end(),
|
||||
[&](std::string x) {
|
||||
return Strings::ToUnsignedInt(x) == skipped->GetPrimaryFaction();
|
||||
}
|
||||
);
|
||||
|
||||
const bool is_excluded = f != excluded.end();
|
||||
|
||||
// If exclude doesn't have a faction, check for buddies based on race.
|
||||
// Also exclude common factions such as noob monsters, indifferent, kos, kos animal
|
||||
if (!is_excluded) {
|
||||
if (n->GetPrimaryFaction() != skipped->GetPrimaryFaction()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (n->GetBaseRace() != skipped->GetBaseRace() || n->IsCharmedPet()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LogFleeDetail(
|
||||
"[{}] on faction [{}] with aggro_range [{}] is at [{}], [{}], [{}] and will count as an ally for [{}]",
|
||||
n->GetName(),
|
||||
n->GetPrimaryFaction(),
|
||||
aggro_range,
|
||||
n->GetX(),
|
||||
n->GetY(),
|
||||
n->GetZ(),
|
||||
skipped->GetName()
|
||||
);
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
|
||||
{
|
||||
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
|
||||
@@ -671,9 +759,9 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
target_owner = nullptr;
|
||||
|
||||
//cannot hurt untargetable mobs
|
||||
bodyType bt = target->GetBodyType();
|
||||
uint8 bt = target->GetBodyType();
|
||||
|
||||
if(bt == BT_NoTarget || bt == BT_NoTarget2) {
|
||||
if(bt == BodyType::NoTarget || bt == BodyType::NoTarget2) {
|
||||
if (RuleB(Pets, UnTargetableSwarmPet)) {
|
||||
if (target->IsNPC()) {
|
||||
if (!target->CastToNPC()->GetSwarmOwner()) {
|
||||
|
||||
@@ -651,7 +651,6 @@ Json::Value ApiGetClientListDetail(EQ::Net::WebsocketServerConnection *connectio
|
||||
row["base_wis"] = client->GetBaseWIS();
|
||||
row["become_npc_level"] = client->GetBecomeNPCLevel();
|
||||
row["boat_id"] = client->GetBoatID();
|
||||
row["buyer_welcome_message"] = client->GetBuyerWelcomeMessage();
|
||||
row["calc_atk"] = client->CalcATK();
|
||||
row["calc_base_mana"] = client->CalcBaseMana();
|
||||
row["calc_current_weight"] = client->CalcCurrentWeight();
|
||||
|
||||
+385
-371
@@ -1478,8 +1478,10 @@ int64 Mob::DoDamageCaps(int64 base_damage)
|
||||
//SYNC WITH: tune.cpp, mob.h TuneDoAttack
|
||||
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte)
|
||||
{
|
||||
if (!other)
|
||||
if (!other) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(),
|
||||
other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill);
|
||||
|
||||
@@ -1491,14 +1493,22 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
|
||||
if (!FromRiposte && other->AvoidDamage(this, hit)) {
|
||||
if (int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
|
||||
strike_through && zone->random.Roll(strike_through)) {
|
||||
MessageString(Chat::StrikeThrough,
|
||||
STRIKETHROUGH_STRING); // You strike through your opponents defenses!
|
||||
|
||||
FilteredMessageString(
|
||||
this, /* Sender */
|
||||
Chat::StrikeThrough, /* Type: 339 */
|
||||
FilterStrikethrough, /* FilterType: 12 */
|
||||
STRIKETHROUGH_STRING /* You strike through your opponent's defenses! */
|
||||
);
|
||||
|
||||
hit.damage_done = 1; // set to one, we will check this to continue
|
||||
}
|
||||
|
||||
if (hit.damage_done == DMG_RIPOSTED) {
|
||||
DoRiposte(other);
|
||||
return;
|
||||
}
|
||||
|
||||
LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done);
|
||||
}
|
||||
|
||||
@@ -1510,9 +1520,19 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
|
||||
int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist;
|
||||
int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
|
||||
if (zone->random.Roll(stun_resist2)) {
|
||||
other->MessageString(Chat::Stun, AVOID_STUNNING_BLOW);
|
||||
other->FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
AVOID_STUNNING_BLOW
|
||||
);
|
||||
} else if (zone->random.Roll(stun_resist)) {
|
||||
other->MessageString(Chat::Stun, SHAKE_OFF_STUN);
|
||||
other->FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
SHAKE_OFF_STUN
|
||||
);
|
||||
} else {
|
||||
other->Stun(3000); // yuck -- 3 seconds
|
||||
}
|
||||
@@ -1530,17 +1550,18 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
|
||||
hit.damage_done = 0;
|
||||
}
|
||||
|
||||
if (IsBot()) {
|
||||
if (parse->BotHasQuestSub(EVENT_USE_SKILL)) {
|
||||
const auto& export_string = fmt::format(
|
||||
parse->EventBotMerc(
|
||||
EVENT_USE_SKILL,
|
||||
this,
|
||||
nullptr,
|
||||
[&]() {
|
||||
return fmt::format(
|
||||
"{} {}",
|
||||
hit.skill,
|
||||
GetSkill(hit.skill)
|
||||
);
|
||||
|
||||
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1719,7 +1740,7 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
(HasOwner() && GetOwner()->IsClient() && other->IsClient())
|
||||
)
|
||||
) {
|
||||
for (auto const& [id, mob] : entity_list.GetCloseMobList(other)) {
|
||||
for (auto const& [id, mob] : other->GetCloseMobList()) {
|
||||
if (!mob) {
|
||||
continue;
|
||||
}
|
||||
@@ -1902,11 +1923,9 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
|
||||
}
|
||||
|
||||
if (killer_mob) {
|
||||
if (killer_mob->IsNPC()) {
|
||||
if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) {
|
||||
parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0);
|
||||
}
|
||||
parse->EventBotMercNPC(EVENT_SLAY, killer_mob, this);
|
||||
|
||||
if (killer_mob->IsNPC()) {
|
||||
killed_by = KilledByTypes::Killed_NPC;
|
||||
|
||||
auto emote_id = killer_mob->GetEmoteID();
|
||||
@@ -1914,12 +1933,6 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
|
||||
killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
|
||||
}
|
||||
|
||||
killer_mob->TrySpellOnKill(killed_level, spell);
|
||||
} else if (killer_mob->IsBot()) {
|
||||
if (parse->BotHasQuestSub(EVENT_SLAY)) {
|
||||
parse->EventBot(EVENT_SLAY, killer_mob->CastToBot(), this, "", 0);
|
||||
}
|
||||
|
||||
killer_mob->TrySpellOnKill(killed_level, spell);
|
||||
}
|
||||
|
||||
@@ -2186,6 +2199,19 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::CheckIfAlreadyDead()
|
||||
{
|
||||
if (!ClientFinishedLoading()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
return false; //cant die more than once...
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
|
||||
bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
||||
{
|
||||
@@ -2286,8 +2312,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
//Guard Assist Code
|
||||
if (RuleB(Character, PVPEnableGuardFactionAssist)) {
|
||||
if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) {
|
||||
auto& mob_list = entity_list.GetCloseMobList(other);
|
||||
for (auto& e : mob_list) {
|
||||
for (auto& e : other->GetCloseMobList()) {
|
||||
auto mob = e.second;
|
||||
if (!mob) {
|
||||
continue;
|
||||
@@ -2426,14 +2451,10 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
|
||||
spell_id = SPELL_UNKNOWN;
|
||||
|
||||
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
|
||||
if (attacked_timer.Check())
|
||||
{
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_ATTACK)) {
|
||||
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr");
|
||||
|
||||
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
|
||||
}
|
||||
if (attacked_timer.Check()) {
|
||||
parse->EventMercNPC(EVENT_ATTACK, this, other);
|
||||
}
|
||||
|
||||
attacked_timer.Start(CombatEventTimer_expire);
|
||||
|
||||
if (!IsEngaged())
|
||||
@@ -2460,11 +2481,6 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
|
||||
|
||||
//do a majority of the work...
|
||||
CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic, special);
|
||||
|
||||
if (damage > 0) {
|
||||
//see if we are gunna start fleeing
|
||||
if (!IsPet()) CheckFlee();
|
||||
}
|
||||
}
|
||||
|
||||
bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by, bool is_buff_tic)
|
||||
@@ -2479,41 +2495,22 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
|
||||
|
||||
if (IsNPC()) {
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
auto exports = [&]() {
|
||||
return fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
};
|
||||
|
||||
if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
if (parse->EventBotMercNPC(EVENT_DEATH, this, owner_or_self, exports) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
} else if (IsBot()) {
|
||||
if (parse->BotHasQuestSub(EVENT_DEATH)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
if (parse->EventBot(EVENT_DEATH, CastToBot(), owner_or_self, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) {
|
||||
@@ -2945,8 +2942,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
entity_list.UnMarkNPC(GetID());
|
||||
entity_list.RemoveNPC(GetID());
|
||||
|
||||
// entity_list.RemoveMobFromCloseLists(this);
|
||||
close_mobs.clear();
|
||||
m_close_mobs.clear();
|
||||
SetID(0);
|
||||
ApplyIllusionToCorpse(illusion_spell_id, corpse);
|
||||
|
||||
@@ -3051,10 +3047,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
}
|
||||
|
||||
if (killer_mob && killer_mob->IsBot()) {
|
||||
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) {
|
||||
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
|
||||
}
|
||||
if (killer_mob) {
|
||||
parse->EventBotMerc(EVENT_NPC_SLAY, killer_mob, this);
|
||||
|
||||
killer_mob->TrySpellOnKill(killed_level, spell);
|
||||
}
|
||||
@@ -3082,24 +3076,29 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
}
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill),
|
||||
entity_id,
|
||||
m_combat_record.GetStartTime(),
|
||||
m_combat_record.GetEndTime(),
|
||||
m_combat_record.GetDamageReceived(),
|
||||
m_combat_record.GetHealingReceived()
|
||||
);
|
||||
std::vector<std::any> args = { corpse };
|
||||
|
||||
std::vector<std::any> args = { corpse };
|
||||
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args);
|
||||
}
|
||||
parse->EventMercNPC(
|
||||
EVENT_DEATH_COMPLETE,
|
||||
this,
|
||||
owner_or_self,
|
||||
[&]() {
|
||||
return fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill),
|
||||
entity_id,
|
||||
m_combat_record.GetStartTime(),
|
||||
m_combat_record.GetEndTime(),
|
||||
m_combat_record.GetDamageReceived(),
|
||||
m_combat_record.GetHealingReceived()
|
||||
);
|
||||
},
|
||||
0,
|
||||
&args
|
||||
);
|
||||
|
||||
// Zone controller process EVENT_DEATH_ZONE (Death events)
|
||||
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
|
||||
@@ -4132,6 +4131,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
AddToHateList(attacker, 0, damage, true, false, iBuffTic, spell_id);
|
||||
}
|
||||
|
||||
bool died = false;
|
||||
if (damage > 0) {
|
||||
//if there is some damage being done and theres an attacker involved
|
||||
int previous_hp_ratio = GetHPRatio();
|
||||
@@ -4256,110 +4256,89 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
}
|
||||
|
||||
//final damage has been determined.
|
||||
SetHP(int64(GetHP() - damage));
|
||||
int old_hp_ratio = (int)GetHPRatio();
|
||||
|
||||
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
|
||||
|
||||
const auto has_bot_taken_event = parse->BotHasQuestSub(EVENT_DAMAGE_TAKEN);
|
||||
|
||||
const auto has_npc_given_event = (
|
||||
(
|
||||
IsNPC() &&
|
||||
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
|
||||
) ||
|
||||
(
|
||||
attacker &&
|
||||
attacker->IsNPC() &&
|
||||
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
|
||||
)
|
||||
);
|
||||
|
||||
const auto has_npc_taken_event = (
|
||||
(
|
||||
IsNPC() &&
|
||||
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
|
||||
) ||
|
||||
(
|
||||
attacker &&
|
||||
attacker->IsNPC() &&
|
||||
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
|
||||
)
|
||||
);
|
||||
|
||||
const auto has_player_given_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_GIVEN);
|
||||
|
||||
const auto has_player_taken_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_TAKEN);
|
||||
|
||||
const auto has_given_event = (
|
||||
has_bot_given_event ||
|
||||
has_npc_given_event ||
|
||||
has_player_given_event
|
||||
);
|
||||
|
||||
const auto has_taken_event = (
|
||||
has_bot_taken_event ||
|
||||
has_npc_taken_event ||
|
||||
has_player_taken_event
|
||||
);
|
||||
|
||||
std::vector<std::any> args;
|
||||
|
||||
if (has_taken_event) {
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
attacker ? attacker->GetID() : 0,
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(skill_used),
|
||||
FromDamageShield ? 1 : 0,
|
||||
avoidable ? 1 : 0,
|
||||
buffslot,
|
||||
iBuffTic ? 1 : 0,
|
||||
static_cast<int>(special)
|
||||
);
|
||||
int64 damage_override = 0;
|
||||
|
||||
if (IsBot() && has_bot_taken_event) {
|
||||
parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0);
|
||||
} else if (IsClient() && has_player_taken_event) {
|
||||
args.push_back(attacker ? attacker : nullptr);
|
||||
parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
|
||||
} else if (IsNPC() && has_npc_taken_event) {
|
||||
parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
|
||||
}
|
||||
if (attacker) {
|
||||
args = { this };
|
||||
|
||||
parse->EventMob(
|
||||
EVENT_DAMAGE_GIVEN,
|
||||
attacker,
|
||||
this,
|
||||
[&]() {
|
||||
return fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
GetID(),
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(skill_used),
|
||||
FromDamageShield ? 1 : 0,
|
||||
avoidable ? 1 : 0,
|
||||
buffslot,
|
||||
iBuffTic ? 1 : 0,
|
||||
static_cast<int>(special)
|
||||
);
|
||||
},
|
||||
0,
|
||||
&args
|
||||
);
|
||||
}
|
||||
|
||||
if (has_given_event && attacker) {
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
GetID(),
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(skill_used),
|
||||
FromDamageShield ? 1 : 0,
|
||||
avoidable ? 1 : 0,
|
||||
buffslot,
|
||||
iBuffTic ? 1 : 0,
|
||||
static_cast<int>(special)
|
||||
);
|
||||
args = { attacker };
|
||||
|
||||
if (attacker->IsBot() && has_bot_given_event) {
|
||||
parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0);
|
||||
} else if (attacker->IsClient() && has_player_given_event) {
|
||||
args.push_back(this);
|
||||
parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args);
|
||||
} else if (attacker->IsNPC() && has_npc_given_event) {
|
||||
parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0);
|
||||
}
|
||||
damage_override = parse->EventMob(
|
||||
EVENT_DAMAGE_TAKEN,
|
||||
this,
|
||||
attacker,
|
||||
[&]() {
|
||||
return fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
attacker ? attacker->GetID() : 0,
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(skill_used),
|
||||
FromDamageShield ? 1 : 0,
|
||||
avoidable ? 1 : 0,
|
||||
buffslot,
|
||||
iBuffTic ? 1 : 0,
|
||||
static_cast<int>(special)
|
||||
);
|
||||
},
|
||||
0,
|
||||
&args
|
||||
);
|
||||
|
||||
if (damage_override > 0) {
|
||||
damage = damage_override;
|
||||
} else if (damage_override < 0) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
SetHP(int64(GetHP() - damage));
|
||||
|
||||
if (HasDied()) {
|
||||
bool IsSaved = false;
|
||||
|
||||
if (TryDivineSave())
|
||||
if (TryDivineSave()) {
|
||||
IsSaved = true;
|
||||
}
|
||||
|
||||
if (!IsSaved && !TrySpellOnDeath()) {
|
||||
SetHP(-500);
|
||||
if (IsNPC()) {
|
||||
died = !CastToNPC()->GetDepop();
|
||||
} else if (IsClient()) {
|
||||
died = CastToClient()->CheckIfAlreadyDead();
|
||||
}
|
||||
|
||||
if (died) {
|
||||
SetHP(-500);
|
||||
}
|
||||
|
||||
// killedByType is clarified in Client::Death if we are client.
|
||||
if (Death(attacker, damage, spell_id, skill_used, KilledByTypes::Killed_NPC, iBuffTic)) {
|
||||
return;
|
||||
@@ -4485,32 +4464,59 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
Stun(RuleI(Combat, StunDuration));
|
||||
if (RuleB(Combat, ClientStunMessage) && attacker->IsClient()) {
|
||||
if (attacker) {
|
||||
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned after being bashed by %s.", GetCleanName(), attacker->GetCleanName());
|
||||
}
|
||||
else {
|
||||
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned by a bash to the head.", GetCleanName());
|
||||
entity_list.FilteredMessageClose(
|
||||
this,
|
||||
true,
|
||||
RuleI(Range, StunMessages),
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
"%s is stunned after being bashed by %s.",
|
||||
GetCleanName(),
|
||||
attacker->GetCleanName()
|
||||
);
|
||||
} else {
|
||||
entity_list.FilteredMessageClose(
|
||||
this,
|
||||
true,
|
||||
RuleI(Range, StunMessages),
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
"%s is stunned by a bash to the head.",
|
||||
GetCleanName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// stun resist passed!
|
||||
if (IsClient())
|
||||
MessageString(Chat::Stun, SHAKE_OFF_STUN);
|
||||
if (IsClient()) {
|
||||
FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
SHAKE_OFF_STUN
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// stun resist 2 passed!
|
||||
if (IsClient()) {
|
||||
FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
AVOID_STUNNING_BLOW
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// stun resist 2 passed!
|
||||
if (IsClient())
|
||||
MessageString(Chat::Stun, AVOID_STUNNING_BLOW);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// main stun failed -- extra interrupt roll
|
||||
if (IsCasting() &&
|
||||
!EQ::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded
|
||||
// 90% chance >< -- stun immune won't reach this branch though :(
|
||||
if (zone->random.Int(0, 9) > 1)
|
||||
// these spells are excluded
|
||||
// 90% chance >< -- stun immune won't reach this branch though :(
|
||||
if (IsCasting() && !EQ::ValueWithin(casting_spell_id, 859, 1023)) {
|
||||
if (zone->random.Int(0, 9) > 1) {
|
||||
InterruptSpell();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4529,8 +4535,21 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
}
|
||||
|
||||
//send an HP update if we are hurt
|
||||
if (GetHP() < GetMaxHP()) {
|
||||
SendHPUpdate(); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them
|
||||
if(GetHP() < GetMaxHP())
|
||||
{
|
||||
// Don't send a HP update for melee damage unless we've damaged ourself.
|
||||
if (IsNPC()) {
|
||||
int cur_hp_ratio = (int)GetHPRatio();
|
||||
if (cur_hp_ratio != old_hp_ratio) {
|
||||
SendHPUpdate(true);
|
||||
}
|
||||
} else if (!iBuffTic || died) { // Let regen handle buff tics unless this tic killed us.
|
||||
SendHPUpdate(true);
|
||||
}
|
||||
|
||||
if (!died && IsNPC()) {
|
||||
CheckFlee();
|
||||
}
|
||||
}
|
||||
} //end `if damage was done`
|
||||
|
||||
@@ -4593,10 +4612,10 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
//this was done to simplify the code here (since we can only effectively skip one mob on queue)
|
||||
eqFilterType filter;
|
||||
Mob* skip = attacker;
|
||||
if (attacker && attacker->IsPet() && !attacker->IsBot()) {
|
||||
Mob* owner = attacker ? attacker->GetOwner() : nullptr;
|
||||
if (attacker && owner && !attacker->IsBot()) {
|
||||
//attacker is a pet, let pet owners see their pet's damage
|
||||
Mob* owner = attacker->GetOwner();
|
||||
if (owner && owner->IsClient()) {
|
||||
if (owner->IsClient()) {
|
||||
if (FromDamageShield && damage > 0) {
|
||||
//special crap for spell damage, looks hackish to me
|
||||
char val1[20] = { 0 };
|
||||
@@ -5388,101 +5407,6 @@ void Mob::TryPetCriticalHit(Mob *defender, DamageHitInfo &hit)
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::RollMeleeCritCheck(Mob *defender, EQ::skills::SkillType skill)
|
||||
{
|
||||
// We either require an innate crit chance or some SPA 169 to crit
|
||||
bool innate_crit = false;
|
||||
int crit_chance = GetCriticalChanceBonus(skill);
|
||||
// Paladin check
|
||||
if (defender->IsUndeadForSlay()) {
|
||||
crit_chance = crit_chance + GetUndeadSlayRate();
|
||||
}
|
||||
|
||||
if (GetLevel() >= 12) {
|
||||
if (
|
||||
GetClass() == Class::Warrior ||
|
||||
(GetClass() == Class::Ranger && skill == EQ::skills::SkillArchery) ||
|
||||
(GetClass() == Class::Rogue && skill == EQ::skills::SkillThrowing) ||
|
||||
GetClass() == Class::Berserker
|
||||
) {
|
||||
innate_crit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// we have a chance to crit!
|
||||
if (innate_crit || crit_chance) {
|
||||
int difficulty = 0;
|
||||
|
||||
if (skill == EQ::skills::SkillArchery) {
|
||||
difficulty = RuleI(Combat, ArcheryCritDifficulty);
|
||||
} else if (skill == EQ::skills::SkillThrowing) {
|
||||
difficulty = RuleI(Combat, ThrowingCritDifficulty);
|
||||
} else {
|
||||
difficulty = RuleI(Combat, MeleeCritDifficulty);
|
||||
}
|
||||
|
||||
int roll = zone->random.Int(1, difficulty);
|
||||
|
||||
int dex_bonus = GetDEX();
|
||||
|
||||
if (dex_bonus > 255) {
|
||||
dex_bonus = 255 + ((dex_bonus - 255) / 5);
|
||||
}
|
||||
|
||||
dex_bonus += 45; // chances did not match live without a small boost
|
||||
|
||||
// so if we have an innate crit we have a better chance, except for ber throwing
|
||||
if (!innate_crit || (GetClass() == Class::Berserker && skill == EQ::skills::SkillThrowing)) {
|
||||
dex_bonus = dex_bonus * 3 / 5;
|
||||
}
|
||||
|
||||
LogCombat("Crit Chance: dex_bonus ({}) * crit_chance ({}) / 100", dex_bonus, crit_chance);
|
||||
|
||||
if (crit_chance) {
|
||||
dex_bonus += dex_bonus * crit_chance / 100;
|
||||
}
|
||||
|
||||
// check if we crited
|
||||
LogCombat("Final Roll! Difficulty = [{}] -- Dex_Bonus = [{}] ", difficulty, dex_bonus);
|
||||
return (roll < dex_bonus);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Mob::GetUndeadSlayRate()
|
||||
{
|
||||
return aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0];
|
||||
}
|
||||
|
||||
void Mob::DoUndeadSlay(DamageHitInfo &hit, int crit_mod)
|
||||
{
|
||||
|
||||
int slay_damage_bonus = std::max(
|
||||
{ aabonuses.SlayUndead[1], itembonuses.SlayUndead[1], spellbonuses.SlayUndead[1] });
|
||||
|
||||
LogCombatDetail("Slayundead bonus [{}]", slay_damage_bonus);
|
||||
|
||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||
hit.damage_done = (hit.damage_done * slay_damage_bonus * crit_mod) / 100;
|
||||
hit.damage_done = static_cast<int>(hit.damage_done * RuleR(Combat, SlayDamageAdjustment));
|
||||
|
||||
LogCombatDetail("Slayundead damage [{}]", hit.damage_done);
|
||||
|
||||
int slay_sex = GetGender() == Gender::Female ? FEMALE_SLAYUNDEAD : MALE_SLAYUNDEAD;
|
||||
|
||||
entity_list.FilteredMessageString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
slay_sex, /* MessageFormat: %1's holy blade cleanses her target!(%2) */
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
}
|
||||
|
||||
// a lot of good info: http://giline.versus.jp/shiden/damage_e.htm, http://giline.versus.jp/shiden/su.htm
|
||||
void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts)
|
||||
{
|
||||
#ifdef LUA_EQEMU
|
||||
@@ -5494,8 +5418,9 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hit.damage_done < 1 || !defender)
|
||||
if (hit.damage_done < 1 || !defender) {
|
||||
return;
|
||||
}
|
||||
|
||||
// decided to branch this into it's own function since it's going to be duplicating a lot of the
|
||||
// code in here, but could lead to some confusion otherwise
|
||||
@@ -5513,112 +5438,201 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Check if we are critting
|
||||
if (!RollMeleeCritCheck(defender, hit.skill)) {
|
||||
return;
|
||||
}
|
||||
// 1: Try Slay Undead
|
||||
if (defender->GetBodyType() == BodyType::Undead || defender->GetBodyType() == BodyType::SummonedUndead ||
|
||||
defender->GetBodyType() == BodyType::Vampire) {
|
||||
int slay_rate_bonus = aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD];
|
||||
|
||||
int crit_mod = EQ::ClampLower((170 + GetCritDmgMod(hit.skill)), 100);
|
||||
LogCombatDetail("Slayundead hit rate [{}]", slay_rate_bonus);
|
||||
|
||||
// Step 2: Calculate damage
|
||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||
int og_damage = hit.damage_done;
|
||||
hit.damage_done = hit.damage_done * crit_mod / 100;
|
||||
if (slay_rate_bonus) {
|
||||
float slay_chance = ((static_cast<float>(slay_rate_bonus) / 10000.0f) * RuleR(Combat, SlayRateMultiplier));
|
||||
|
||||
LogCombatDetail("Crit info: [{}] scaled from: [{}] - IsUndeadForSlay: [{}]", hit.damage_done, og_damage, IsUndeadForSlay() ? "true" : "false");
|
||||
if (zone->random.Roll(slay_chance)) {
|
||||
int slay_damage_bonus = aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD];
|
||||
|
||||
// Try Slay Undead
|
||||
if (defender->IsUndeadForSlay()) {
|
||||
float chance = GetUndeadSlayRate() / 100.0f;
|
||||
LogCombatDetail("Trying Undead slay: Chance: [{}]", chance);
|
||||
LogCombatDetail("Slayundead damage bonus [{}]", slay_damage_bonus);
|
||||
|
||||
if(zone->random.Roll(chance)) {
|
||||
DoUndeadSlay(hit, crit_mod);
|
||||
return;
|
||||
}
|
||||
}
|
||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||
hit.damage_done = (hit.damage_done * slay_damage_bonus) / 100;
|
||||
hit.damage_done = static_cast<int>(hit.damage_done * RuleR(Combat, SlayDamageMultiplier));
|
||||
|
||||
// Step 3: Check deadly strike
|
||||
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing && BehindMob(defender, GetX(), GetY())) {
|
||||
int chance = GetLevel() * 12;
|
||||
if (zone->random.Int(1, 1000) < chance) {
|
||||
// Check assassinate
|
||||
int assassinate_damage = TryAssassinate(defender, hit.skill);
|
||||
int min_slay = (hit.min_damage + 5) * slay_damage_bonus / 100;
|
||||
|
||||
if (assassinate_damage) {
|
||||
hit.damage_done = assassinate_damage;
|
||||
return;
|
||||
}
|
||||
LogCombatDetail(" Calculated Slayundead damage [{}] - Min Slay Undead Damage [{}]", hit.damage_done, min_slay);
|
||||
|
||||
hit.damage_done = hit.damage_done * 200 / 100;
|
||||
if (hit.damage_done < min_slay) {
|
||||
hit.damage_done = min_slay;
|
||||
}
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
LogCombatDetail("Final Slayundead damage [{}]", hit.damage_done);
|
||||
|
||||
int slay_sex = GetGender() == Gender::Female ? FEMALE_SLAYUNDEAD : MALE_SLAYUNDEAD;
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
DEADLY_STRIKE, /* MessageFormat: %1 scores a Deadly Strike!(%2) */
|
||||
slay_sex,
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
return;
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: check cripple
|
||||
bool berserk = spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA;
|
||||
// 2: Try Melee Critical
|
||||
// a lot of good info: http://giline.versus.jp/shiden/damage_e.htm, http://giline.versus.jp/shiden/su.htm
|
||||
|
||||
if (!berserk && zone->random.Roll(GetCrippBlowChance())) {
|
||||
berserk = true;
|
||||
// We either require an innate crit chance or some SPA 169 to crit
|
||||
bool innate_crit = false;
|
||||
int crit_chance = GetCriticalChanceBonus(hit.skill);
|
||||
if ((GetClass() == Class::Warrior || GetClass() == Class::Berserker) && GetLevel() >= 12) {
|
||||
innate_crit = true;
|
||||
} else if (GetClass() == Class::Ranger && GetLevel() >= 12 && hit.skill == EQ::skills::SkillArchery) {
|
||||
innate_crit = true;
|
||||
} else if (GetClass() == Class::Rogue && GetLevel() >= 12 && hit.skill == EQ::skills::SkillThrowing) {
|
||||
innate_crit = true;
|
||||
}
|
||||
|
||||
if (IsBerserk() || berserk) {
|
||||
hit.damage_done += og_damage * 119 / 100;
|
||||
LogCombatDetail("Crippling damage [{}]", hit.damage_done);
|
||||
// we have a chance to crit!
|
||||
if (innate_crit || crit_chance) {
|
||||
int difficulty = 0;
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
if (hit.skill == EQ::skills::SkillArchery) {
|
||||
difficulty = RuleI(Combat, ArcheryCritDifficulty);
|
||||
} else if (hit.skill == EQ::skills::SkillThrowing) {
|
||||
difficulty = RuleI(Combat, ThrowingCritDifficulty);
|
||||
} else {
|
||||
difficulty = RuleI(Combat, MeleeCritDifficulty);
|
||||
}
|
||||
|
||||
int roll = zone->random.Int(1, difficulty);
|
||||
int dex_bonus = GetDEX();
|
||||
|
||||
if (dex_bonus > 255) {
|
||||
dex_bonus = 255 + ((dex_bonus - 255) / 5);
|
||||
}
|
||||
|
||||
dex_bonus += 45; // chances did not match live without a small boost
|
||||
|
||||
// so if we have an innate crit we have a better chance, except for ber throwing
|
||||
if (!innate_crit || (GetClass() == Class::Berserker && hit.skill == EQ::skills::SkillThrowing)) {
|
||||
dex_bonus = dex_bonus * 3 / 5;
|
||||
}
|
||||
|
||||
if (crit_chance) {
|
||||
dex_bonus += dex_bonus * crit_chance / 100;
|
||||
}
|
||||
|
||||
// check if we crited
|
||||
if (roll < dex_bonus) {
|
||||
// step 1: check for finishing blow
|
||||
if (TryFinishingBlow(defender, hit.damage_done)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// step 2: calculate damage
|
||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||
int og_damage = hit.damage_done;
|
||||
int crit_mod = 170 + GetCritDmgMod(hit.skill);
|
||||
|
||||
if (crit_mod < 100) {
|
||||
crit_mod = 100;
|
||||
}
|
||||
|
||||
hit.damage_done = hit.damage_done * crit_mod / 100;
|
||||
LogCombatDetail("Crit success roll [{}] dex chance [{}] og dmg [{}] crit_mod [{}] new dmg [{}]", roll, dex_bonus, og_damage, crit_mod, hit.damage_done);
|
||||
|
||||
// step 3: check deadly strike
|
||||
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing) {
|
||||
if (BehindMob(defender, GetX(), GetY())) {
|
||||
int chance = GetLevel() * 12;
|
||||
if (zone->random.Int(1, 1000) < chance) {
|
||||
// step 3a: check assassinate
|
||||
int assassinate_damage = TryAssassinate(defender, hit.skill); // I don't think this is right
|
||||
if (assassinate_damage) {
|
||||
hit.damage_done = assassinate_damage;
|
||||
return;
|
||||
}
|
||||
hit.damage_done = hit.damage_done * 200 / 100;
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
DEADLY_STRIKE, /* MessageFormat: %1 scores a Deadly Strike!(%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 4: check crips
|
||||
// this SPA was reused on live ...
|
||||
bool berserk = spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA;
|
||||
if (!berserk) {
|
||||
if (zone->random.Roll(GetCrippBlowChance())) {
|
||||
berserk = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsBerserk() || berserk) {
|
||||
hit.damage_done += og_damage * 119 / 100;
|
||||
LogCombat("Crip damage [{}]", hit.damage_done);
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
CRIPPLING_BLOW, /* MessageFormat: %1 lands a Crippling Blow!(%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||
);
|
||||
|
||||
// Crippling blows also have a chance to stun
|
||||
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
|
||||
// staggers message.
|
||||
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(SpecialAbility::StunImmunity)) {
|
||||
entity_list.MessageCloseString(
|
||||
defender,
|
||||
true,
|
||||
RuleI(Range, Emote),
|
||||
Chat::Emote,
|
||||
STAGGERS,
|
||||
GetName()
|
||||
);
|
||||
defender->Stun(RuleI(Combat, StunDuration));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Normal Critical hit message */
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
CRIPPLING_BLOW, /* MessageFormat: %1 lands a Crippling Blow!(%2) */
|
||||
CRITICAL_HIT, /* MessageFormat: %1 scores a critical hit! (%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
|
||||
// Crippling blows also have a chance to stun
|
||||
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
|
||||
// staggers message.
|
||||
if (defender->GetLevel() <= RuleI(Combat, MaximumLevelStunsCripplingBlow) && !defender->GetSpecialAbility(SpecialAbility::StunImmunity)) {
|
||||
entity_list.MessageCloseString(
|
||||
defender,
|
||||
true,
|
||||
RuleI(Range, Emote),
|
||||
Chat::Emote,
|
||||
STAGGERS,
|
||||
GetName()
|
||||
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||
);
|
||||
defender->Stun(RuleI(Combat, StunDuration));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Normal Critical hit message */
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
CRITICAL_HIT, /* MessageFormat: %1 scores a critical hit! (%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
}
|
||||
|
||||
bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
|
||||
|
||||
+7
-12
@@ -72,7 +72,7 @@ Mob *Aura::GetOwner()
|
||||
// not 100% sure how this one should work and PVP affects ...
|
||||
void Aura::ProcessOnAllFriendlies(Mob *owner)
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this, distance);
|
||||
auto &mob_list = GetCloseMobList(distance);
|
||||
std::set<int> delayed_remove;
|
||||
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
|
||||
|
||||
@@ -127,7 +127,7 @@ void Aura::ProcessOnAllFriendlies(Mob *owner)
|
||||
|
||||
void Aura::ProcessOnAllGroupMembers(Mob *owner)
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this, distance);
|
||||
auto &mob_list = GetCloseMobList(distance);
|
||||
std::set<int> delayed_remove;
|
||||
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
|
||||
|
||||
@@ -369,7 +369,7 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner)
|
||||
|
||||
void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this,distance);
|
||||
auto &mob_list = GetCloseMobList(distance);
|
||||
std::set<int> delayed_remove;
|
||||
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
|
||||
// This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura)
|
||||
@@ -576,7 +576,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||
|
||||
void Aura::ProcessTotem(Mob *owner)
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this, distance);
|
||||
auto &mob_list = GetCloseMobList(distance);
|
||||
std::set<int> delayed_remove;
|
||||
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
|
||||
|
||||
@@ -634,9 +634,7 @@ void Aura::ProcessTotem(Mob *owner)
|
||||
|
||||
void Aura::ProcessEnterTrap(Mob *owner)
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this, distance);
|
||||
|
||||
for (auto &e : mob_list) {
|
||||
for (auto &e : GetCloseMobList(distance)) {
|
||||
auto mob = e.second;
|
||||
if (!mob) {
|
||||
continue;
|
||||
@@ -656,9 +654,7 @@ void Aura::ProcessEnterTrap(Mob *owner)
|
||||
|
||||
void Aura::ProcessExitTrap(Mob *owner)
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this, distance);
|
||||
|
||||
for (auto &e : mob_list) {
|
||||
for (auto &e : GetCloseMobList(distance)) {
|
||||
auto mob = e.second;
|
||||
if (!mob) {
|
||||
continue;
|
||||
@@ -689,8 +685,7 @@ void Aura::ProcessExitTrap(Mob *owner)
|
||||
// and hard to reason about
|
||||
void Aura::ProcessSpawns()
|
||||
{
|
||||
const auto &clients = entity_list.GetCloseMobList(this, distance);
|
||||
for (auto &e : clients) {
|
||||
for (auto &e: GetCloseMobList(distance)) {
|
||||
if (!e.second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ Beacon::Beacon(const glm::vec4 &in_pos, int lifetime) : Mob(
|
||||
Gender::Male, // in_gender
|
||||
Race::InvisibleMan, // in_race
|
||||
Class::None, // in_class
|
||||
BT_NoTarget, // in_bodytype
|
||||
BodyType::NoTarget, // in_bodytype
|
||||
Deity::Unknown, // in_deity
|
||||
0, // in_level
|
||||
0, // in_npctype_id
|
||||
|
||||
+22
-12
@@ -182,14 +182,20 @@ void Mob::CalcItemBonuses(StatBonuses* b) {
|
||||
SetDualWeaponsEquipped(true);
|
||||
}
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
for (i = EQ::invslot::TRIBUTE_BEGIN; i <= EQ::invslot::TRIBUTE_END; i++) {
|
||||
const EQ::ItemInstance* inst = m_inv[i];
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
if (IsClient()) {
|
||||
if (CastToClient()->GetPP().tribute_active) {
|
||||
for (auto const &t: CastToClient()->GetPP().tributes) {
|
||||
auto item_id = CastToClient()->LookupTributeItemID(t.tribute, t.tier);
|
||||
if (item_id) {
|
||||
const EQ::ItemInstance *inst = database.CreateItem(item_id);
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddItemBonuses(inst, b, false, true);
|
||||
AddItemBonuses(inst, b, false, true);
|
||||
safe_delete(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1439,8 +1445,8 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
|
||||
case SE_SlayUndead: {
|
||||
if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base_value) {
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = base_value; // Rate
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base_value; // Rate
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = limit_value; // Damage Modifier
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2438,6 +2444,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
case SE_CastingLevel2:
|
||||
{
|
||||
new_bonus->effective_casting_level += effect_value;
|
||||
|
||||
if (RuleB(Spells, SnareOverridesSpeedBonuses) && effect_value < 0) {
|
||||
new_bonus->movementspeed = effect_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3356,7 +3366,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
|
||||
case SE_Blind:
|
||||
if (RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
||||
if (!RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3585,8 +3595,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
|
||||
case SE_SlayUndead: {
|
||||
if (new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < effect_value) {
|
||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; // Rate
|
||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier
|
||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = limit_value; // Rate
|
||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; // Damage Modifier
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+16
-27
@@ -204,7 +204,7 @@ Bot::Bot(
|
||||
);
|
||||
}
|
||||
|
||||
SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == EQ::constants::stanceAggressive));
|
||||
SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == Stance::Aggressive));
|
||||
SetPauseAI(false);
|
||||
|
||||
m_auto_defend_timer.Disable();
|
||||
@@ -1578,15 +1578,8 @@ bool Bot::Process()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mob_close_scan_timer.Check()) {
|
||||
LogAIScanCloseDetail(
|
||||
"is_moving [{}] bot [{}] timer [{}]",
|
||||
moving ? "true" : "false",
|
||||
GetCleanName(),
|
||||
mob_close_scan_timer.GetDuration()
|
||||
);
|
||||
|
||||
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
|
||||
if (m_scan_close_mobs_timer.Check()) {
|
||||
entity_list.ScanCloseMobs(this);
|
||||
}
|
||||
|
||||
SpellProcess();
|
||||
@@ -1899,8 +1892,8 @@ void Bot::AI_Process()
|
||||
#define NOT_GUARDING (!GetGuardFlag())
|
||||
#define HOLDING (GetHoldFlag())
|
||||
#define NOT_HOLDING (!GetHoldFlag())
|
||||
#define PASSIVE (GetBotStance() == EQ::constants::stancePassive)
|
||||
#define NOT_PASSIVE (GetBotStance() != EQ::constants::stancePassive)
|
||||
#define PASSIVE (GetBotStance() == Stance::Passive)
|
||||
#define NOT_PASSIVE (GetBotStance() != Stance::Passive)
|
||||
|
||||
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
|
||||
|
||||
@@ -2978,7 +2971,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) {
|
||||
if (NOT_HOLDING && NOT_PASSIVE) {
|
||||
|
||||
auto attack_target = bot_owner->GetTarget();
|
||||
if (attack_target) {
|
||||
if (attack_target && HasBotAttackFlag(attack_target)) {
|
||||
|
||||
InterruptSpell();
|
||||
WipeHateList();
|
||||
@@ -5481,13 +5474,13 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) {
|
||||
if (!Result) {
|
||||
if (caster->IsBot()) {
|
||||
if (spells[spell_id].target_type == ST_Undead) {
|
||||
if ((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Undead) && (GetBodyType() != BT_Vampire)) {
|
||||
if ((GetBodyType() != BodyType::SummonedUndead) && (GetBodyType() != BodyType::Undead) && (GetBodyType() != BodyType::Vampire)) {
|
||||
LogSpellsDetail("Bot's target is not an undead");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (spells[spell_id].target_type == ST_Summoned) {
|
||||
if ((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Summoned) && (GetBodyType() != BT_Summoned2) && (GetBodyType() != BT_Summoned3)) {
|
||||
if ((GetBodyType() != BodyType::SummonedUndead) && (GetBodyType() != BodyType::Summoned) && (GetBodyType() != BodyType::Summoned2) && (GetBodyType() != BodyType::Summoned3)) {
|
||||
LogSpellsDetail("Bot's target is not a summoned creature");
|
||||
return true;
|
||||
}
|
||||
@@ -6912,16 +6905,16 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
|
||||
if ((botCasterClass == Class::Paladin || botCasterClass == Class::Beastlord || botCasterClass == Class::Ranger) && (caster->HasGroup() || caster->IsRaidGrouped())) {
|
||||
float hpRatioToHeal = 25.0f;
|
||||
switch(caster->GetBotStance()) {
|
||||
case EQ::constants::stanceReactive:
|
||||
case EQ::constants::stanceBalanced:
|
||||
case Stance::Reactive:
|
||||
case Stance::Balanced:
|
||||
hpRatioToHeal = 50.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBurn:
|
||||
case EQ::constants::stanceBurnAE:
|
||||
case Stance::Burn:
|
||||
case Stance::AEBurn:
|
||||
hpRatioToHeal = 20.0f;
|
||||
break;
|
||||
case EQ::constants::stanceAggressive:
|
||||
case EQ::constants::stanceEfficient:
|
||||
case Stance::Aggressive:
|
||||
case Stance::Efficient:
|
||||
default:
|
||||
hpRatioToHeal = 25.0f;
|
||||
break;
|
||||
@@ -7655,11 +7648,7 @@ bool Bot::HasOrMayGetAggro() {
|
||||
}
|
||||
|
||||
void Bot::SetDefaultBotStance() {
|
||||
EQ::constants::StanceType defaultStance = EQ::constants::stanceBalanced;
|
||||
if (GetClass() == Class::Warrior)
|
||||
defaultStance = EQ::constants::stanceAggressive;
|
||||
|
||||
_botStance = defaultStance;
|
||||
_botStance = GetClass() == Class::Warrior ? Stance::Aggressive : Stance::Balanced;
|
||||
}
|
||||
|
||||
void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) {
|
||||
@@ -9233,4 +9222,4 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id)
|
||||
|
||||
}
|
||||
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 };
|
||||
|
||||
+4
-9
@@ -484,7 +484,7 @@ public:
|
||||
bool IsOfClientBotMerc() const override { return true; }
|
||||
|
||||
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
||||
uint8 GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||
float GetBotCasterMaxRange(float melee_distance_max);
|
||||
@@ -605,12 +605,7 @@ public:
|
||||
void SetPetChooser(bool p) { _petChooser = p; }
|
||||
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
|
||||
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
|
||||
void SetBotStance(EQ::constants::StanceType botStance) {
|
||||
if (botStance >= EQ::constants::stancePassive && botStance <= EQ::constants::stanceBurnAE)
|
||||
_botStance = botStance;
|
||||
else
|
||||
_botStance = EQ::constants::stancePassive;
|
||||
}
|
||||
void SetBotStance(uint8 stance_id) { _botStance = Stance::IsValid(stance_id) ? stance_id : Stance::Passive; }
|
||||
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
||||
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
|
||||
bool CheckSpellRecastTimer(uint16 spell_id = 0);
|
||||
@@ -753,7 +748,7 @@ public:
|
||||
//Raid additions
|
||||
Raid* p_raid_instance;
|
||||
|
||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND];
|
||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND];
|
||||
|
||||
bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||
@@ -870,7 +865,7 @@ private:
|
||||
std::string _suffix;
|
||||
uint32 _lastZoneId;
|
||||
bool _rangerAutoWeaponSelect;
|
||||
EQ::constants::StanceType _botStance;
|
||||
uint8 _botStance;
|
||||
unsigned int RestRegenHP;
|
||||
unsigned int RestRegenMana;
|
||||
unsigned int RestRegenEndurance;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user