From 97bb50a5c4a9738f914be4b173a8af35b0bad758 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 17 Feb 2018 17:35:54 -0500 Subject: [PATCH 01/59] Fix for query failure on some systems --- utils/sql/git/required/2018_02_13_Heading.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/git/required/2018_02_13_Heading.sql b/utils/sql/git/required/2018_02_13_Heading.sql index 4f4736625..e7869b575 100644 --- a/utils/sql/git/required/2018_02_13_Heading.sql +++ b/utils/sql/git/required/2018_02_13_Heading.sql @@ -1,3 +1,3 @@ UPDATE spawn2 SET heading = heading * 8.0 / 4.0; UPDATE grid_entries SET heading = heading * 8.0 / 4.0 WHERE heading <> -1; -INSERT INTO variables (varname, value) VALUES ('fixed_heading', 1); -- hack +INSERT INTO variables (varname, value, information) VALUES ('fixed_heading', 1, 'manifest heading fix hack'); -- hack From a493242c3c9e95f9f20ce9fd1aa3ab6c03459a93 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 18 Feb 2018 14:03:13 -0500 Subject: [PATCH 02/59] Fix and overhaul to bug reporting system --- changelog.txt | 8 + common/emu_constants.cpp | 65 ++++ common/emu_constants.h | 31 ++ common/eq_packet_structs.h | 70 +++-- common/patches/rof2_structs.h | 44 +-- common/patches/rof_structs.h | 44 +-- common/patches/sod.cpp | 19 -- common/patches/sod_ops.h | 1 - common/patches/sod_structs.h | 47 +-- common/patches/sof.cpp | 11 + common/patches/sof_ops.h | 1 + common/patches/sof_structs.h | 67 +++-- common/patches/titanium.cpp | 11 + common/patches/titanium_ops.h | 1 + common/patches/titanium_structs.h | 68 +++-- common/patches/uf_structs.h | 46 +-- common/ruletypes.h | 6 + common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../git/required/2018_02_18_bug_reports.sql | 44 +++ zone/client_packet.cpp | 28 +- zone/zonedb.cpp | 280 +++++++++++++++--- zone/zonedb.h | 5 +- 23 files changed, 618 insertions(+), 282 deletions(-) create mode 100644 utils/sql/git/required/2018_02_18_bug_reports.sql diff --git a/changelog.txt b/changelog.txt index f515e8e49..12a9fc76a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/18/2018 == +Uleat: Bug reporting fix and overhaul. + - Fixed bug reporting for SoD+ clients + - Added ability to disable bug reporting (set rule 'Bugs:ReportingSystemActive' to 'false') + - Implemented a more detailed reporting system (set rule 'Bugs:UseOldReportingMethod' to 'false') + -- New system is not currently compatible with script-based monitoring + - Soft-removal of defunct 'Petition Bug' system + == 02/14/2018 == mackal: Fix Heading -- Quests broken diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index a8fa73d1c..e5e9ae841 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -18,3 +18,68 @@ */ #include "emu_constants.h" + + +const char* EQEmu::bug::CategoryIDToCategoryName(CategoryID category_id) { + switch (category_id) { + case catVideo: + return "Video"; + case catAudio: + return "Audio"; + case catPathing: + return "Pathing"; + case catQuest: + return "Quest"; + case catTradeskills: + return "Tradeskills"; + case catSpellStacking: + return "Spell stacking"; + case catDoorsPortals: + return "Doors/Portals"; + case catItems: + return "Items"; + case catNPC: + return "NPC"; + case catDialogs: + return "Dialogs"; + case catLoNTCG: + return "LoN - TCG"; + case catMercenaries: + return "Mercenaries"; + case catOther: + default: + return "Other"; + } +} + +EQEmu::bug::CategoryID EQEmu::bug::CategoryNameToCategoryID(const char* category_name) { + if (!category_name) + return catOther; + + if (!strcmp(category_name, "Video")) + return catVideo; + if (!strcmp(category_name, "Audio")) + return catAudio; + if (!strcmp(category_name, "Pathing")) + return catPathing; + if (!strcmp(category_name, "Quest")) + return catQuest; + if (!strcmp(category_name, "Tradeskills")) + return catTradeskills; + if (!strcmp(category_name, "Spell stacking")) + return catSpellStacking; + if (!strcmp(category_name, "Doors/Portals")) + return catDoorsPortals; + if (!strcmp(category_name, "Items")) + return catItems; + if (!strcmp(category_name, "NPC")) + return catNPC; + if (!strcmp(category_name, "Dialogs")) + return catDialogs; + if (!strcmp(category_name, "LoN - TCG")) + return catLoNTCG; + if (!strcmp(category_name, "Mercenaries")) + return catMercenaries; + + return catOther; +} diff --git a/common/emu_constants.h b/common/emu_constants.h index e156fe98a..04e36849b 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -126,6 +126,37 @@ namespace EQEmu } /*constants*/ + namespace bug { + enum CategoryID : uint32 { + catOther = 0, + catVideo, + catAudio, + catPathing, + catQuest, + catTradeskills, + catSpellStacking, + catDoorsPortals, + catItems, + catNPC, + catDialogs, + catLoNTCG, + catMercenaries + }; + + enum OptionalInfoFlag : uint32 { + infoNoOptionalInfo = 0x0, + infoCanDuplicate = 0x1, + infoCrashBug = 0x2, + infoTargetInfo = 0x4, + infoCharacterFlags = 0x8, + infoUnknownValue = 0xFFFFFFF0 + }; + + const char* CategoryIDToCategoryName(CategoryID category_id); + CategoryID CategoryNameToCategoryID(const char* category_name); + + } // namespace bug + enum class CastingSlot : uint32 { Gem1 = 0, Gem2 = 1, diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8452813f5..8b8949255 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3323,23 +3323,32 @@ struct GuildMakeLeader{ char target[64]; }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ char unknown308[160]; -/*0468*/ char target_name[64]; -/*0532*/ uint32 type; -/*0536*/ char unknown536[2052]; -/*2584*/ char bug[2048]; -/*4632*/ char unknown4632[6]; -/*4638*/ char system_info[4094]; +struct BugReport_Struct { +/*0000*/ uint32 category_id; +/*0004*/ char category_name[64]; +/*0068*/ char reporter_name[64]; +/*0132*/ char unused_0132[32]; +/*0164*/ char ui_path[128]; +/*0292*/ float pos_x; +/*0296*/ float pos_y; +/*0300*/ float pos_z; +/*0304*/ uint32 heading; +/*0308*/ uint32 unused_0308; +/*0312*/ uint32 time_played; +/*0316*/ char padding_0316[8]; +/*0324*/ uint32 target_id; +/*0328*/ char padding_0328[140]; +/*0468*/ uint32 unknown_0468; // seems to always be '0' +/*0472*/ char target_name[64]; +/*0536*/ uint32 optional_info_mask; + +// this looks like a butchered 8k buffer with 2 trailing dword fields +/*0540*/ char unused_0540[2052]; +/*2592*/ char bug_report[2050]; +/*4642*/ char system_info[4098]; +/*8740*/ }; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3366,20 +3375,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 5d42d9046..ecc288a35 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -3605,21 +3605,6 @@ struct GuildSetRank_Struct /*80*/ }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3646,20 +3631,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 8de4fb9ab..bd9cbbce9 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -3545,21 +3545,6 @@ struct GuildSetRank_Struct /*80*/ }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3586,20 +3571,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index a77feedbc..ceba224f4 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -2933,25 +2933,6 @@ namespace SoD FINISH_DIRECT_DECODE(); } - DECODE(OP_Bug) - { - DECODE_LENGTH_EXACT(structs::BugStruct); - SETUP_DIRECT_DECODE(BugStruct, structs::BugStruct); - - strn0cpy(emu->chartype, eq->chartype, sizeof(emu->chartype)); - strn0cpy(emu->name, eq->name, sizeof(emu->name)); - strn0cpy(emu->ui, eq->ui, sizeof(emu->ui)); - IN(x); - IN(y); - IN(z); - IN(heading); - strn0cpy(emu->target_name, eq->target_name, sizeof(emu->target_name)); - strn0cpy(emu->bug, eq->bug, sizeof(emu->bug)); - strn0cpy(emu->system_info, eq->system_info, sizeof(emu->system_info)); - - FINISH_DIRECT_DECODE(); - } - DECODE(OP_CastSpell) { DECODE_LENGTH_EXACT(structs::CastSpell_Struct); diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index 90c28b2eb..7c1109499 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -104,7 +104,6 @@ D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_BazaarSearch) D(OP_Buff) -D(OP_Bug) D(OP_CastSpell) D(OP_ChannelMessage) D(OP_CharacterCreate) diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 88a78c6ee..76635108d 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3008,24 +3008,6 @@ struct GuildMakeLeader{ char target[64]; }; -struct BugStruct{ -/*0000*/ uint32 type1; //seems to be just a different way of seeing type; seems to be ordered completely differently -/*0004*/ char chartype[64]; -/*0068*/ char name[96]; -/*0164*/ char ui[128]; -/*0292*/ float x; -/*0296*/ float y; -/*0300*/ float z; -/*0304*/ float heading; -/*0308*/ uint32 unknown304; -/*0312*/ char unknown308[160]; -/*0472*/ char target_name[64]; -/*0536*/ uint32 type; -/*0540*/ char unknown536[2052]; -/*2588*/ char bug[2048]; -/*4636*/ char unknown4632[6]; -/*4642*/ char system_info[4094]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3052,20 +3034,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 9df6ca1df..bee75b6c2 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2385,6 +2385,17 @@ namespace SoF FINISH_DIRECT_DECODE(); } + DECODE(OP_Bug) + { + DECODE_LENGTH_EXACT(structs::BugReport_Struct); + SETUP_DIRECT_DECODE(BugReport_Struct, structs::BugReport_Struct); + + emu->category_id = EQEmu::bug::CategoryNameToCategoryID(eq->category_name); + memcpy(emu->category_name, eq, sizeof(structs::BugReport_Struct)); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_CastSpell) { DECODE_LENGTH_EXACT(structs::CastSpell_Struct); diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index 180857388..c4e42d8d4 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -95,6 +95,7 @@ D(OP_ApplyPoison) D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_Buff) +D(OP_Bug) D(OP_CastSpell) D(OP_ChannelMessage) D(OP_CharacterCreate) diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 9d87bd83a..ca1c93ed2 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -2906,23 +2906,31 @@ struct GuildMakeLeader{ char target[64]; }; +struct BugReport_Struct { +/*0000*/ char category_name[64]; +/*0064*/ char character_name[64]; +/*0128*/ char unused_0128[32]; +/*0160*/ char ui_path[128]; +/*0288*/ float pos_x; +/*0292*/ float pos_y; +/*0296*/ float pos_z; +/*0300*/ uint32 heading; +/*0304*/ uint32 unused_0304; +/*0308*/ uint32 time_played; +/*0312*/ char padding_0312[8]; +/*0320*/ uint32 target_id; +/*0324*/ char padding_0324[140]; +/*0464*/ uint32 unknown_0464; // seems to always be '0' +/*0468*/ char target_name[64]; +/*0532*/ uint32 optional_info_mask; - -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; +// this looks like a butchered 8k buffer with 2 trailing dword fields +/*0536*/ char unused_0536[2052]; +/*2588*/ char bug_report[2050]; +/*4638*/ char system_info[4098]; +/*8736*/ }; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -2949,20 +2957,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 45f267223..2c010713d 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1789,6 +1789,17 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_Bug) + { + DECODE_LENGTH_EXACT(structs::BugReport_Struct); + SETUP_DIRECT_DECODE(BugReport_Struct, structs::BugReport_Struct); + + emu->category_id = EQEmu::bug::CategoryNameToCategoryID(eq->category_name); + memcpy(emu->category_name, eq, sizeof(structs::BugReport_Struct)); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_CastSpell) { DECODE_LENGTH_EXACT(structs::CastSpell_Struct); diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 9f77a7077..dafb8b903 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -78,6 +78,7 @@ D(OP_AdventureMerchantSell) D(OP_ApplyPoison) D(OP_AugmentItem) D(OP_Buff) +D(OP_Bug) D(OP_CastSpell) D(OP_ChannelMessage) D(OP_CharacterCreate) diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index e52b7628c..41f029c8d 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -2571,21 +2571,32 @@ struct GuildMakeLeader{ char name[64]; char target[64]; }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; + +struct BugReport_Struct { +/*0000*/ char category_name[64]; +/*0064*/ char character_name[64]; +/*0128*/ char unused_0128[32]; +/*0160*/ char ui_path[128]; +/*0288*/ float pos_x; +/*0292*/ float pos_y; +/*0296*/ float pos_z; +/*0300*/ uint32 heading; +/*0304*/ uint32 unused_0304; +/*0308*/ uint32 time_played; +/*0312*/ char padding_0312[8]; +/*0320*/ uint32 target_id; +/*0324*/ char padding_0324[140]; +/*0464*/ uint32 unknown_0464; // seems to always be '0' +/*0468*/ char target_name[64]; +/*0532*/ uint32 optional_info_mask; + +// this looks like a butchered 8k buffer with 2 trailing dword fields +/*0536*/ char unused_0536[2052]; +/*2588*/ char bug_report[2050]; +/*4638*/ char system_info[4098]; +/*8736*/ }; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -2612,20 +2623,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index cc5564e11..b10e3a3dd 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -3060,23 +3060,6 @@ struct GuildMakeLeader{ char target[64]; }; - - -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3103,20 +3086,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; diff --git a/common/ruletypes.h b/common/ruletypes.h index 24d9e809b..219995913 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -720,6 +720,12 @@ RULE_BOOL(Client, UseLiveFactionMessage, false) // Allows players to see faction RULE_BOOL(Client, UseLiveBlockedMessage, false) // Allows players to see faction adjustments like Live RULE_CATEGORY_END() +RULE_CATEGORY(Bugs) +RULE_BOOL(Bugs, ReportingSystemActive, true) // Activates bug reporting +RULE_BOOL(Bugs, UseOldReportingMethod, true) // Forces the use of the old bug reporting system +RULE_BOOL(Bugs, DumpTargetEntity, false) // Dumps the target entity, if one is provided +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/common/version.h b/common/version.h index 412223b8c..d1afd9744 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9120 +#define CURRENT_BINARY_DATABASE_VERSION 9121 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9018 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 4de5f3d8f..2a0b12fd2 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -374,6 +374,7 @@ 9118|2018_02_04_Charm_Stats.sql|SHOW COLUMNS FROM `npc_types` LIKE 'charm_ac'|empty| 9119|2018_02_10_GlobalLoot.sql|SHOW TABLES LIKE 'global_loot'|empty| 9120|2018_02_13_Heading.sql|SELECT value FROM variables WHERE varname = 'fixed_heading'|empty| +9121|2018_02_18_bug_reports.sql|SHOW TABLES LIKE 'bug_reports'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2018_02_18_bug_reports.sql b/utils/sql/git/required/2018_02_18_bug_reports.sql new file mode 100644 index 000000000..92afbd7c9 --- /dev/null +++ b/utils/sql/git/required/2018_02_18_bug_reports.sql @@ -0,0 +1,44 @@ +CREATE TABLE `bug_reports` ( + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `zone` VARCHAR(32) NOT NULL DEFAULT 'Unknown', + `client_version_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `client_version_name` VARCHAR(24) NOT NULL DEFAULT 'Unknown', + `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `character_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `character_name` VARCHAR(64) NOT NULL DEFAULT 'Unknown', + `reporter_spoof` TINYINT(1) NOT NULL DEFAULT '1', + `category_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `category_name` VARCHAR(64) NOT NULL DEFAULT 'Other', + `reporter_name` VARCHAR(64) NOT NULL DEFAULT 'Unknown', + `ui_path` VARCHAR(128) NOT NULL DEFAULT 'Unknown', + `pos_x` FLOAT NOT NULL DEFAULT '0', + `pos_y` FLOAT NOT NULL DEFAULT '0', + `pos_z` FLOAT NOT NULL DEFAULT '0', + `heading` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `time_played` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `target_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `target_name` VARCHAR(64) NOT NULL DEFAULT 'Unknown', + `optional_info_mask` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `_can_duplicate` TINYINT(1) NOT NULL DEFAULT '0', + `_crash_bug` TINYINT(1) NOT NULL DEFAULT '0', + `_target_info` TINYINT(1) NOT NULL DEFAULT '0', + `_character_flags` TINYINT(1) NOT NULL DEFAULT '0', + `_unknown_value` TINYINT(1) NOT NULL DEFAULT '0', + `bug_report` VARCHAR(1024) NOT NULL DEFAULT '', + `system_info` VARCHAR(1024) NOT NULL DEFAULT '', + `report_datetime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `bug_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `last_review` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `last_reviewer` VARCHAR(64) NOT NULL DEFAULT 'None', + `reviewer_notes` VARCHAR(1024) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE INDEX `id` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB +; + +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES +(1, 'Bugs:ReportingSystemActive', 'true', 'Activates bug reporting'), +(1, 'Bugs:UseOldReportingMethod', 'true', 'Forces the use of the old bug reporting system'), +(1, 'Bugs:DumpTargetEntity', 'false', 'Dumps the target entity, if one is provided'); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2de71acc8..07085ee71 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3960,12 +3960,23 @@ void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) void Client::Handle_OP_Bug(const EQApplicationPacket *app) { - if (app->size != sizeof(BugStruct)) - printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); - else { - BugStruct* bug = (BugStruct*)app->pBuffer; - database.UpdateBug(bug); + if (!RuleB(Bugs, ReportingSystemActive)) { + Message(0, "Bug reporting is disabled on this server."); + return; } + + if (app->size != sizeof(BugReport_Struct)) { + printf("Wrong size of BugReport_Struct got %d expected %zu!\n", app->size, sizeof(BugReport_Struct)); + } + else { + BugReport_Struct* bug_report = (BugReport_Struct*)app->pBuffer; + + if (RuleB(Bugs, UseOldReportingMethod)) + database.RegisterBug(bug_report); + else + database.RegisterBug(this, bug_report); + } + return; } @@ -10553,11 +10564,8 @@ void Client::Handle_OP_Petition(const EQApplicationPacket *app) void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) { - if (app->size != sizeof(PetitionBug_Struct)) - printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size); - else { - Message(0, "Petition Bugs are not supported, please use /bug."); - } + Message(0, "Petition Bugs are not supported, please use /bug."); + return; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 01add9944..d5db09a53 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -325,62 +325,260 @@ bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 stat return true; } +void ZoneDatabase::RegisterBug(BugReport_Struct* bug_report) { + if (!bug_report) + return; -void ZoneDatabase::UpdateBug(BugStruct* bug) { - - uint32 len = strlen(bug->bug); - char* bugtext = nullptr; - if(len > 0) - { - bugtext = new char[2*len+1]; - memset(bugtext, 0, 2*len+1); - DoEscapeString(bugtext, bug->bug, len); + size_t len = 0; + char* name_ = nullptr; + char* ui_ = nullptr; + char* type_ = nullptr; + char* target_ = nullptr; + char* bug_ = nullptr; + + len = strlen(bug_report->reporter_name); + if (len) { + if (len > 63) // check against db column size + len = 63; + name_ = new char[(2 * len + 1)]; + memset(name_, 0, (2 * len + 1)); + DoEscapeString(name_, bug_report->reporter_name, len); } - len = strlen(bug->ui); - char* uitext = nullptr; - if(len > 0) - { - uitext = new char[2*len+1]; - memset(uitext, 0, 2*len+1); - DoEscapeString(uitext, bug->ui, len); + len = strlen(bug_report->ui_path); + if (len) { + if (len > 127) + len = 127; + ui_ = new char[(2 * len + 1)]; + memset(ui_, 0, (2 * len + 1)); + DoEscapeString(ui_, bug_report->ui_path, len); } - len = strlen(bug->target_name); - char* targettext = nullptr; - if(len > 0) - { - targettext = new char[2*len+1]; - memset(targettext, 0, 2*len+1); - DoEscapeString(targettext, bug->target_name, len); + len = strlen(bug_report->category_name); + if (len) { + if (len > 63) + len = 63; + type_ = new char[(2 * len + 1)]; + memset(type_, 0, (2 * len + 1)); + DoEscapeString(type_, bug_report->category_name, len); } - //x and y are intentionally swapped because eq is inversexy coords - std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " + len = strlen(bug_report->target_name); + if (len) { + if (len > 63) + len = 63; + target_ = new char[(2 * len + 1)]; + memset(target_, 0, (2 * len + 1)); + DoEscapeString(target_, bug_report->target_name, len); + } + + len = strlen(bug_report->bug_report); + if (len) { + if (len > 1023) + len = 1023; + bug_ = new char[(2 * len + 1)]; + memset(bug_, 0, (2 * len + 1)); + DoEscapeString(bug_, bug_report->bug_report, len); + } + + //x and y are intentionally swapped because eq is inversexy coords //is this msg out-of-date or are the parameters wrong? + std::string query = StringFormat( + "INSERT INTO `bugs` (`zone`, `name`, `ui`, `x`, `y`, `z`, `type`, `flag`, `target`, `bug`, `date`) " "VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", - zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext, - bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext, - bugtext==nullptr?"":bugtext); - safe_delete_array(bugtext); - safe_delete_array(uitext); - safe_delete_array(targettext); + zone->GetShortName(), + (name_ ? name_ : ""), + (ui_ ? ui_ : ""), + bug_report->pos_x, + bug_report->pos_y, + bug_report->pos_z, + (type_ ? type_ : ""), + bug_report->optional_info_mask, + (target_ ? target_ : "Unknown Target"), + (bug_ ? bug_ : "") + ); + safe_delete_array(name_); + safe_delete_array(ui_); + safe_delete_array(type_); + safe_delete_array(target_); + safe_delete_array(bug_); + QueryDatabase(query); } -void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ +void ZoneDatabase::RegisterBug(Client* client, BugReport_Struct* bug_report) { + if (!client || !bug_report) + return; - uint32 len = strlen(bug->text); - auto bugtext = new char[2 * len + 1]; - memset(bugtext, 0, 2*len+1); - DoEscapeString(bugtext, bug->text, len); + size_t len = 0; + char* category_name_ = nullptr; + char* reporter_name_ = nullptr; + char* ui_path_ = nullptr; + char* target_name_ = nullptr; + char* bug_report_ = nullptr; + char* system_info_ = nullptr; - std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " - "VALUES('%s', '%s', '%s', %i)", - "Petition", bug->name, bugtext, 25); - safe_delete_array(bugtext); - QueryDatabase(query); + len = strlen(bug_report->category_name); + if (len) { + if (len > 63) // check against db column size + len = 63; + category_name_ = new char[(2 * len + 1)]; + memset(category_name_, 0, (2 * len + 1)); + DoEscapeString(category_name_, bug_report->category_name, len); + } + + len = strlen(bug_report->reporter_name); + if (len) { + if (len > 63) + len = 63; + reporter_name_ = new char[(2 * len + 1)]; + memset(reporter_name_, 0, (2 * len + 1)); + DoEscapeString(reporter_name_, bug_report->reporter_name, len); + } + + len = strlen(bug_report->ui_path); + if (len) { + if (len > 127) + len = 127; + ui_path_ = new char[(2 * len + 1)]; + memset(ui_path_, 0, (2 * len + 1)); + DoEscapeString(ui_path_, bug_report->ui_path, len); + } + + len = strlen(bug_report->target_name); + if (len) { + if (len > 63) + len = 63; + target_name_ = new char[(2 * len + 1)]; + memset(target_name_, 0, (2 * len + 1)); + DoEscapeString(target_name_, bug_report->target_name, len); + } + + len = strlen(bug_report->bug_report); + if (len) { + if (len > 1023) + len = 1023; + bug_report_ = new char[(2 * len + 1)]; + memset(bug_report_, 0, (2 * len + 1)); + DoEscapeString(bug_report_, bug_report->bug_report, len); + } + + len = strlen(bug_report->system_info); + if (len) { + if (len > 1023) + len = 1023; + system_info_ = new char[(2 * len + 1)]; + memset(system_info_, 0, (2 * len + 1)); + DoEscapeString(system_info_, bug_report->system_info, len); + } + + std::string query = StringFormat( + "INSERT INTO `bug_reports` " + "(`zone`," + " `client_version_id`," + " `client_version_name`," + " `account_id`," + " `character_id`," + " `character_name`," + " `reporter_spoof`," + " `category_id`," + " `category_name`," + " `reporter_name`," + " `ui_path`," + " `pos_x`," + " `pos_y`," + " `pos_z`," + " `heading`," + " `time_played`," + " `target_id`," + " `target_name`," + " `optional_info_mask`," + " `_can_duplicate`," + " `_crash_bug`," + " `_target_info`," + " `_character_flags`," + " `_unknown_value`," + " `bug_report`," + " `system_info`) " + "VALUES " + "('%s'," + " '%u'," + " '%s'," + " '%u'," + " '%u'," + " '%s'," + " '%u'," + " '%u'," + " '%s'," + " '%s'," + " '%s'," + " '%1.1f'," + " '%1.1f'," + " '%1.1f'," + " '%u'," + " '%u'," + " '%u'," + " '%s'," + " '%u'," + " '%u'," + " '%u'," + " '%u'," + " '%u'," + " '%u'," + " '%s'," + " '%s')", + zone->GetShortName(), + client->ClientVersion(), + EQEmu::versions::ClientVersionName(client->ClientVersion()), + client->AccountID(), + client->CharacterID(), + client->GetName(), + (strcmp(client->GetName(), reporter_name_) != 0 ? 1 : 0), + bug_report->category_id, + (category_name_ ? category_name_ : ""), + (reporter_name_ ? reporter_name_ : ""), + (ui_path_ ? ui_path_ : ""), + bug_report->pos_x, + bug_report->pos_y, + bug_report->pos_z, + bug_report->heading, + bug_report->time_played, + bug_report->target_id, + (target_name_ ? target_name_ : ""), + bug_report->optional_info_mask, + ((bug_report->optional_info_mask & EQEmu::bug::infoCanDuplicate) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoCrashBug) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoTargetInfo) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoCharacterFlags) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoUnknownValue) != 0 ? 1 : 0), + (bug_report_ ? bug_report_ : ""), + (system_info_ ? system_info_ : "") + ); + safe_delete_array(category_name_); + safe_delete_array(reporter_name_); + safe_delete_array(ui_path_); + safe_delete_array(target_name_); + safe_delete_array(bug_report_); + safe_delete_array(system_info_); + + auto result = QueryDatabase(query); + + // TODO: Entity dumping [RuleB(Bugs, DumpTargetEntity)] } +//void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug) { +// +// uint32 len = strlen(bug->text); +// auto bugtext = new char[2 * len + 1]; +// memset(bugtext, 0, 2 * len + 1); +// DoEscapeString(bugtext, bug->text, len); +// +// std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " +// "VALUES('%s', '%s', '%s', %i)", +// "Petition", bug->name, bugtext, 25); +// safe_delete_array(bugtext); +// QueryDatabase(query); +//} + bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); diff --git a/zone/zonedb.h b/zone/zonedb.h index 6f65bae91..d0e7b1a99 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -442,8 +442,9 @@ public: bool DeleteMerc(uint32 merc_id); /* Petitions */ - void UpdateBug(BugStruct* bug); - void UpdateBug(PetitionBug_Struct* bug); + void RegisterBug(BugReport_Struct* bug_report); // old method + void RegisterBug(Client* client, BugReport_Struct* bug_report); // new method + //void UpdateBug(PetitionBug_Struct* bug); void DeletePetitionFromDB(Petition* wpet); void UpdatePetitionToDB(Petition* wpet); void InsertPetitionToDB(Petition* wpet); From c36a1cd6dc1dad49af831d40db9627fa7b4964ea Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 18 Feb 2018 14:30:58 -0500 Subject: [PATCH 03/59] Fix for linux build fail --- common/emu_constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index 04e36849b..0858fec72 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -24,7 +24,7 @@ #include "emu_legacy.h" #include "emu_versions.h" -#include +#include namespace EQEmu From 765ee5eeedb75291113aec307825431678beb68a Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 19 Feb 2018 07:34:43 -0500 Subject: [PATCH 04/59] Fix for possible memory leak when spawning bots --- zone/bot.cpp | 24 ++++++++++++++++-------- zone/bot.h | 2 +- zone/bot_command.cpp | 23 ++++++++++++++++++----- zone/bot_database.cpp | 5 ++++- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b0435dbe4..95ecf7885 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3053,7 +3053,7 @@ void Bot::Depop() { NPC::Depop(false); } -void Bot::Spawn(Client* botCharacterOwner) { +bool Bot::Spawn(Client* botCharacterOwner) { if(GetBotID() > 0 && _botOwnerCharacterID > 0 && botCharacterOwner && botCharacterOwner->CharacterID() == _botOwnerCharacterID) { // Rename the bot name to make sure that Mob::GetName() matches Mob::GetCleanName() so we dont have a bot named "Jesuschrist001" strcpy(name, GetCleanName()); @@ -3097,7 +3097,11 @@ void Bot::Spawn(Client* botCharacterOwner) { this->SendWearChange(materialFromSlot); } } + + return true; } + + return false; } // Deletes the inventory record for the specified item from the database for this bot. @@ -3246,16 +3250,20 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { if(!ActiveBots.empty()) { for(std::list::iterator itr = ActiveBots.begin(); itr != ActiveBots.end(); ++itr) { Bot* activeBot = Bot::LoadBot(*itr); + if (!activeBot) + continue; - if(activeBot) { - activeBot->Spawn(botOwner); - g->UpdatePlayer(activeBot); - // follow the bot owner, not the group leader we just zoned with our owner. - if(g->IsGroupMember(botOwner) && g->IsGroupMember(activeBot)) - activeBot->SetFollowID(botOwner->GetID()); + if (!activeBot->Spawn(botOwner)) { + safe_delete(activeBot); + continue; } - if(activeBot && !botOwner->HasGroup()) + g->UpdatePlayer(activeBot); + // follow the bot owner, not the group leader we just zoned with our owner. + if (g->IsGroupMember(botOwner) && g->IsGroupMember(activeBot)) + activeBot->SetFollowID(botOwner->GetID()); + + if(!botOwner->HasGroup()) database.SetGroupID(activeBot->GetCleanName(), 0, activeBot->GetBotID()); } } diff --git a/zone/bot.h b/zone/bot.h index c69405011..1d38e5f57 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -275,7 +275,7 @@ public: static bool IsValidRaceClassCombo(uint16 r, uint8 c); bool IsValidName(); static bool IsValidName(std::string& name); - void Spawn(Client* botCharacterOwner); + bool Spawn(Client* botCharacterOwner); virtual void SetLevel(uint8 in_level, bool command = false); virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); virtual bool Process(); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index e94825929..cd5e727d0 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -5049,7 +5049,11 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) return; } - my_bot->Spawn(c); + if (!my_bot->Spawn(c)) { + c->Message(m_fail, "Failed to spawn bot '%s' (id: %i)", bot_name.c_str(), bot_id); + safe_delete(my_bot); + return; + } static const char* bot_spawn_message[16] = { "A solid weapon is my ally!", // WARRIOR / 'generic' @@ -5805,18 +5809,22 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) return; } if (!leader_id) { - c->Message(m_fail, "Can not locate bot-group leader id for '%s'", botgroup_name_arg.c_str()); + c->Message(m_fail, "Cannot locate bot-group leader id for '%s'", botgroup_name_arg.c_str()); return; } auto botgroup_leader = Bot::LoadBot(leader_id); if (!botgroup_leader) { - c->Message(m_fail, "Could not spawn bot-group leader for '%s'", botgroup_name_arg.c_str()); + c->Message(m_fail, "Could not load bot-group leader for '%s'", botgroup_name_arg.c_str()); safe_delete(botgroup_leader); return; } - botgroup_leader->Spawn(c); + if (!botgroup_leader->Spawn(c)) { + c->Message(m_fail, "Could not spawn bot-group leader %s for '%s'", botgroup_leader->GetName(), botgroup_name_arg.c_str()); + safe_delete(botgroup_leader); + return; + } Group* group_inst = new Group(botgroup_leader); @@ -5835,7 +5843,12 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) return; } - botgroup_member->Spawn(c); + if (!botgroup_member->Spawn(c)) { + c->Message(m_fail, "Could not spawn bot '%s' (id: %i)", botgroup_member->GetName(), member_iter); + safe_delete(botgroup_member); + return; + } + Bot::AddBotToGroup(botgroup_member, group_inst); } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index d8511c54c..4c5aabb82 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -273,7 +273,10 @@ bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name, if (!owner_id || bot_name.empty()) return false; - query = StringFormat("SELECT `bot_id` FROM `bot_data` WHERE `name` = '%s' LIMIT 1", bot_name.c_str()); + query = StringFormat( + "SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u' AND `name` = '%s' LIMIT 1", + owner_id, bot_name.c_str() + ); auto results = QueryDatabase(query); if (!results.Success()) return false; From 0b755cac546bfb04b2d4d252c56409e9547117f3 Mon Sep 17 00:00:00 2001 From: Xackery Date: Wed, 21 Feb 2018 07:59:47 -0800 Subject: [PATCH 05/59] Added sanity check for #hotfix --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index 179a37ded..dcfecce7a 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -10835,7 +10835,7 @@ void command_hotfix(Client *c, const Seperator *sep) { } worldserver.SendPacket(&pack); - c->Message(0, "Hotfix applied"); + if (c) c->Message(0, "Hotfix applied"); }); t1.detach(); From 6027a80c142f9be4af2417928df979d782670a41 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 22 Feb 2018 01:30:40 -0500 Subject: [PATCH 06/59] Identified bouding_radius in all spawn structs --- common/eq_packet_structs.h | 2 +- common/patches/rof2_structs.h | 2 +- common/patches/rof_structs.h | 2 +- common/patches/sod_structs.h | 2 +- common/patches/sof_structs.h | 3 ++- common/patches/titanium_structs.h | 2 +- common/patches/uf_structs.h | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8b8949255..26ef0bee9 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -280,7 +280,7 @@ union // horse: 0=brown, 1=white, 2=black, 3=tan }; /*0340*/ uint32 spawnId; // Spawn Id -/*0344*/ uint8 unknown0344[3]; +/*0344*/ float bounding_radius; // used in melee, overrides calc /*0347*/ uint8 IsMercenary; /*0348*/ EQEmu::TintProfile equipment_tint; /*0384*/ uint8 lfg; // 0=off, 1=lfg on diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index ecc288a35..c516929f4 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -430,7 +430,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index bd9cbbce9..db52827b9 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -408,7 +408,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 76635108d..1000cb5a3 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -292,7 +292,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index ca1c93ed2..f6298f41d 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -326,7 +326,8 @@ union /*0725*/ uint8 targetable; // 1 = Targetable 0 = Not Targetable (is_npc?) /*0726*/ uint8 unknown0726[4]; /*0730*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse -/*0731*/ uint8 unknown0731[11]; +/*0731*/ float bounding_radius; // used in melee, overrides calc +/*0735*/ uint8 unknown0731[7]; /*0742*/ uint8 targetable_with_hotkey; /*0743*/ signed padding00:12; // ***Placeholder signed x:19; // x coord diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 41f029c8d..3a2787500 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -327,7 +327,7 @@ union // horse: 0=brown, 1=white, 2=black, 3=tan }; /*0340*/ uint32 spawnId; // Spawn Id -/*0344*/ uint8 unknown0344[4]; +/*0344*/ float bounding_radius; // used in melee, overrides calc /*0348*/ TintProfile equipment_tint; /*0384*/ uint8 lfg; // 0=off, 1=lfg on /*0385*/ diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index b10e3a3dd..256169d67 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -292,7 +292,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable From f3b2ac6c17665c326be8d5bdef91492c0ace0b27 Mon Sep 17 00:00:00 2001 From: Xackery Date: Thu, 22 Feb 2018 18:43:31 -0800 Subject: [PATCH 07/59] Added rule to remove pet reagent cost --- common/ruletypes.h | 1 + zone/spells.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 219995913..b6acffc3c 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -156,6 +156,7 @@ RULE_BOOL(Character, UseOldBindWound, false) // Uses the original bind wound beh RULE_BOOL(Character, GrantHoTTOnCreate, false) // Grant Health of Target's Target leadership AA on character creation RULE_BOOL(Character, UseOldConSystem, false) // Grant Health of Target's Target leadership AA on character creation RULE_BOOL(Character, OPClientUpdateVisualDebug, false) // Shows a pulse and forward directional particle each time the client sends its position to server +RULE_BOOL(Character, PetsUseReagents, true) //Pets use reagent on spells RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/spells.cpp b/zone/spells.cpp index 78ef657aa..e66a41fa8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1208,7 +1208,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // handle the components for traditional casters else { - if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found + if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + //bypass reagent cost + } + else if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found { if (!missingreags) { @@ -1238,6 +1241,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo return; } } + else if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + //bypass reagent cost + } else if (!bard_song_mode) { int noexpend; From ca0b9bc3749ee29fda4af2fc12751681dc85da79 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 23 Feb 2018 17:00:17 -0500 Subject: [PATCH 08/59] Fix rampage behavior Fixes #716 --- common/features.h | 3 +++ zone/mob_ai.cpp | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/common/features.h b/common/features.h index 486255f28..a35431bff 100644 --- a/common/features.h +++ b/common/features.h @@ -219,6 +219,9 @@ enum { //some random constants //the square of the maximum range at whihc you could possibly use NPC services (shop, tribute, etc) #define USE_NPC_RANGE2 200*200 //arbitrary right now +// Squared range for rampage 75.0 * 75.0 for now +#define NPC_RAMPAGE_RANGE2 5625.0f + //the formula for experience for killing a mob. //level is the only valid variable to use #define EXP_FORMULA level*level*75*35/10 diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index ed55d1146..b82f8601a 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2074,15 +2074,34 @@ bool Mob::Rampage(ExtraAttackOptions *opts) if (m_target) { if (m_target == GetTarget()) continue; - if (CombatRange(m_target)) { + if (DistanceSquaredNoZ(GetPosition(), m_target->GetPosition()) <= NPC_RAMPAGE_RANGE2) { ProcessAttackRounds(m_target, opts); index_hit++; } } } - if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets) - ProcessAttackRounds(GetTarget(), opts); + if (RuleB(Combat, RampageHitsTarget)) { + if (index_hit < rampage_targets) + ProcessAttackRounds(GetTarget(), opts); + } else { // let's do correct behavior here, if they set above rule we can assume they want non-live like behavior + if (index_hit < rampage_targets) { + // so we go over in reverse order and skip range check + // lets do it this way to still support non-live-like >1 rampage targets + // likely live is just a fall through of the last valid mob + for (auto i = RampageArray.crbegin(); i != RampageArray.crend(); ++i) { + if (index_hit >= rampage_targets) + break; + auto m_target = entity_list.GetMob(*i); + if (m_target) { + if (m_target == GetTarget()) + continue; + ProcessAttackRounds(m_target, opts); + index_hit++; + } + } + } + } m_specialattacks = eSpecialAttacks::None; From 959337b592a5bdd085789567570e6bf6cb3f1755 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 23 Feb 2018 17:09:04 -0500 Subject: [PATCH 09/59] Pets should warp to owner >= 450 units Fixes #715 --- zone/mob_ai.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index b82f8601a..fec331cb5 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -918,29 +918,26 @@ void Client::AI_Process() } } - if(IsPet()) - { - Mob* owner = GetOwner(); - if(owner == nullptr) + if (IsPet()) { + Mob *owner = GetOwner(); + if (owner == nullptr) return; float dist = DistanceSquared(m_Position, owner->GetPosition()); - if (dist >= 400) - { - if(AI_movement_timer->Check()) - { - int nspeed = (dist >= 5625 ? GetRunspeed() : GetWalkspeed()); + if (dist >= 202500) { // >= 450 distance + Teleport(static_cast(owner->GetPosition())); + SendPositionUpdate(); // this shouldn't happen a lot (and hard to make it) so lets not rate limit + } else if (dist >= 400) { // >=20 + if (AI_movement_timer->Check()) { + int nspeed = (dist >= 5625 ? GetRunspeed() : GetWalkspeed()); // >= 75 animation = nspeed; nspeed *= 2; SetCurrentSpeed(nspeed); CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), nspeed); } - } - else - { - if(moved) - { + } else { + if (moved) { SetCurrentSpeed(0); moved = false; } From c87380fa54379bff2d745d24a5d8b8147a77004a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 23 Feb 2018 17:42:57 -0500 Subject: [PATCH 10/59] Actually fix pet warp range ... --- zone/mob_ai.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index fec331cb5..a7d1c2cb4 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1428,7 +1428,7 @@ void Mob::AI_Process() { if (dist >= 5625) speed = GetRunspeed(); - if (distz > 100) + if (dist >= 202500 || distz > 100) // dist >= 450 { m_Position = ownerPos; SendPositionUpdate(); From fc339cc9d01838db95fc4e55f9e741cf08ea2509 Mon Sep 17 00:00:00 2001 From: Kurt Gilpin Date: Sat, 24 Feb 2018 00:23:30 -0600 Subject: [PATCH 11/59] Removed conditional for installing libsodium. If it's superseded or already installed, it just won't install it anyway. --- utils/scripts/linux_installer/install.sh | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/utils/scripts/linux_installer/install.sh b/utils/scripts/linux_installer/install.sh index 1a94320ae..a88be638c 100644 --- a/utils/scripts/linux_installer/install.sh +++ b/utils/scripts/linux_installer/install.sh @@ -122,16 +122,12 @@ if [[ "$OS" == "Debian" ]]; then apt-get $apt_options install libjson-perl # If libsodium18 isn't installed (Debian), let's download both that and the dev package and install them. - if dpkg-query -s "libsodium18" 1>/dev/null 2>&1; then - echo "Sodium library already installed." - else - wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium-dev_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium-dev.deb - wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium18_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium18.deb - dpkg -i /home/eqemu/libsodium*.deb - # Cleanup after ourselves - rm -f /home/eqemu/libsodium-dev.deb - rm -f /home/eqemu/libsodium18.deb - fi + wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium-dev_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium-dev.deb + wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium18_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium18.deb + dpkg -i /home/eqemu/libsodium*.deb + # Cleanup after ourselves + rm -f /home/eqemu/libsodium-dev.deb + rm -f /home/eqemu/libsodium18.deb #::: Install FTP for remote FTP access echo "proftpd-basic shared/proftpd/inetd_or_standalone select standalone" | debconf-set-selections From 46a358abe649a3e5c8d0d2f766be8e985b5b647e Mon Sep 17 00:00:00 2001 From: Kurt Gilpin Date: Sat, 24 Feb 2018 00:24:26 -0600 Subject: [PATCH 12/59] Update install.sh --- utils/scripts/linux_installer/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/scripts/linux_installer/install.sh b/utils/scripts/linux_installer/install.sh index a88be638c..bffa2d939 100644 --- a/utils/scripts/linux_installer/install.sh +++ b/utils/scripts/linux_installer/install.sh @@ -121,7 +121,7 @@ if [[ "$OS" == "Debian" ]]; then apt-get $apt_options install libsodium18 apt-get $apt_options install libjson-perl - # If libsodium18 isn't installed (Debian), let's download both that and the dev package and install them. + # Install libsodium wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium-dev_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium-dev.deb wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium18_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium18.deb dpkg -i /home/eqemu/libsodium*.deb From 361937d4438ad6780e39cd3a5ee86334987d72ce Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 24 Feb 2018 09:08:11 -0500 Subject: [PATCH 13/59] SayLink clean-up --- common/emu_constants.h | 4 ++ common/emu_legacy.h | 2 - common/eq_packet_structs.h | 17 +++++ common/patches/rof.cpp | 70 ++++++++++---------- common/patches/rof2.cpp | 60 ++++++++--------- common/patches/rof2_structs.h | 17 +++++ common/patches/rof_structs.h | 17 +++++ common/patches/sod.cpp | 76 +++++++++++----------- common/patches/sod_structs.h | 16 +++++ common/patches/sof.cpp | 76 +++++++++++----------- common/patches/sof_structs.h | 16 +++++ common/patches/titanium.cpp | 80 +++++++++++------------ common/patches/titanium_structs.h | 15 +++++ common/patches/uf.cpp | 76 +++++++++++----------- common/patches/uf_structs.h | 16 +++++ common/say_link.cpp | 104 +++++++++++++++--------------- common/say_link.h | 65 ++++++++----------- zone/bot.cpp | 2 + zone/bot_command.cpp | 8 +-- zone/client.cpp | 2 +- zone/command.cpp | 83 +++++++----------------- zone/corpse.cpp | 8 +-- zone/inventory.cpp | 2 +- zone/npc.cpp | 4 +- zone/questmgr.cpp | 12 ++-- zone/tasks.cpp | 3 +- 26 files changed, 455 insertions(+), 396 deletions(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index 0858fec72..32968458c 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -114,7 +114,11 @@ namespace EQEmu const EQEmu::versions::ClientVersion CharacterCreationClient = EQEmu::versions::ClientVersion::RoF2; const size_t CharacterCreationMax = RoF2::constants::CharacterCreationLimit; + const size_t SayLinkOpenerSize = 1; const size_t SayLinkBodySize = RoF2::constants::SayLinkBodySize; + const size_t SayLinkTextSize = 256; // this may be varied until it breaks something (tested:374) - the others are constant + const size_t SayLinkCloserSize = 1; + const size_t SayLinkMaximumSize = (SayLinkOpenerSize + SayLinkBodySize + SayLinkTextSize + SayLinkCloserSize); const int LongBuffs = RoF2::constants::LongBuffs; const int ShortBuffs = RoF2::constants::ShortBuffs; diff --git a/common/emu_legacy.h b/common/emu_legacy.h index 0f3cf0a62..812a33ca1 100644 --- a/common/emu_legacy.h +++ b/common/emu_legacy.h @@ -175,8 +175,6 @@ namespace EQEmu // POTION_BELT_SIZE sets maximum limit..active limit will need to be handled by the appropriate AA or spell (or item?) static const size_t POTION_BELT_ITEM_COUNT = 5; - - static const size_t TEXT_LINK_BODY_LENGTH = 56; } } diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 26ef0bee9..cc06ecf15 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5366,6 +5366,23 @@ struct AuraDestory_Struct { }; // I think we can assume it's just action for 2, client doesn't seem to do anything with the rest of the data in that case +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char Augment6[5]; +/*036*/ char IsEvolving[1]; +/*037*/ char EvolveGroup[4]; +/*041*/ char EvolveLevel[2]; +/*043*/ char OrnamentIcon[5]; +/*048*/ char Hash[8]; +/*056*/ +}; + // Restore structure packing to default #pragma pack() diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index a57e798db..24fecb2ca 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -57,11 +57,11 @@ namespace RoF static inline uint32 RoFToServerTypelessSlot(structs::TypelessInventorySlot_Struct rofSlot); static inline uint32 RoFToServerCorpseSlot(uint32 rofCorpseSlot); - // server to client text link converter - static inline void ServerToRoFTextLink(std::string& rofTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToRoFSayLink(std::string& rofSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink); + // client to server say link converter + static inline void RoFToServerSayLink(std::string& serverSayLink, const std::string& rofSayLink); static inline CastingSlot ServerToRoFCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot RoFToServerCastingSlot(CastingSlot slot); @@ -520,7 +520,7 @@ namespace RoF std::string old_message = emu->message; std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; @@ -847,7 +847,7 @@ namespace RoF std::string old_message = emu->message; std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -899,7 +899,7 @@ namespace RoF for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToRoFTextLink(new_message_array[i], old_message_array[i]); + ServerToRoFSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -3295,7 +3295,7 @@ namespace RoF std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -3369,7 +3369,7 @@ namespace RoF std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -4355,7 +4355,7 @@ namespace RoF std::string old_message = InBuffer; std::string new_message; - RoFToServerTextLink(new_message, old_message); + RoFToServerSayLink(new_message, old_message); //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -4489,7 +4489,7 @@ namespace RoF std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - RoFToServerTextLink(new_message, old_message); + RoFToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -5916,19 +5916,19 @@ namespace RoF return (rofCorpseSlot - 1); } - static inline void ServerToRoFTextLink(std::string& rofTextLink, const std::string& serverTextLink) + static inline void ServerToRoFSayLink(std::string& rofSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - rofTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + rofSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - rofTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + rofSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -5938,36 +5938,36 @@ namespace RoF // RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55) // Diff: ^ - rofTextLink.push_back('\x12'); - rofTextLink.append(segments[segment_iter].substr(0, 41)); + rofSayLink.push_back('\x12'); + rofSayLink.append(segments[segment_iter].substr(0, 41)); if (segments[segment_iter][41] == '0') - rofTextLink.push_back(segments[segment_iter][42]); + rofSayLink.push_back(segments[segment_iter][42]); else - rofTextLink.push_back('F'); + rofSayLink.push_back('F'); - rofTextLink.append(segments[segment_iter].substr(43)); - rofTextLink.push_back('\x12'); + rofSayLink.append(segments[segment_iter].substr(43)); + rofSayLink.push_back('\x12'); } else { - rofTextLink.append(segments[segment_iter]); + rofSayLink.append(segments[segment_iter]); } } } - static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink) + static inline void RoFToServerSayLink(std::string& serverSayLink, const std::string& rofSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (rofTextLink.find('\x12') == std::string::npos)) { - serverTextLink = rofTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (rofSayLink.find('\x12') == std::string::npos)) { + serverSayLink = rofSayLink; return; } - auto segments = SplitString(rofTextLink, '\x12'); + auto segments = SplitString(rofSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -5977,14 +5977,14 @@ namespace RoF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 41)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(41)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 41)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(41)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 64b991ddc..7e5d79bb6 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -57,11 +57,11 @@ namespace RoF2 static inline uint32 RoF2ToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof2Slot); static inline uint32 RoF2ToServerCorpseSlot(uint32 rof2CorpseSlot); - // server to client text link converter - static inline void ServerToRoF2TextLink(std::string& rof2TextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToRoF2SayLink(std::string& rof2SayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink); + // client to server say link converter + static inline void RoF2ToServerSayLink(std::string& serverSayLink, const std::string& rof2SayLink); static inline CastingSlot ServerToRoF2CastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot RoF2ToServerCastingSlot(CastingSlot slot); @@ -588,7 +588,7 @@ namespace RoF2 std::string old_message = emu->message; std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; @@ -915,7 +915,7 @@ namespace RoF2 std::string old_message = emu->message; std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -967,7 +967,7 @@ namespace RoF2 for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToRoF2TextLink(new_message_array[i], old_message_array[i]); + ServerToRoF2SayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -3364,7 +3364,7 @@ namespace RoF2 std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -3438,7 +3438,7 @@ namespace RoF2 std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -4595,7 +4595,7 @@ namespace RoF2 std::string old_message = InBuffer; std::string new_message; - RoF2ToServerTextLink(new_message, old_message); + RoF2ToServerSayLink(new_message, old_message); //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -4729,7 +4729,7 @@ namespace RoF2 std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - RoF2ToServerTextLink(new_message, old_message); + RoF2ToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -6233,19 +6233,19 @@ namespace RoF2 return (rof2CorpseSlot + EQEmu::legacy::CORPSE_BEGIN - 1); } - static inline void ServerToRoF2TextLink(std::string& rof2TextLink, const std::string& serverTextLink) + static inline void ServerToRoF2SayLink(std::string& rof2SayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - rof2TextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + rof2SayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - rof2TextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + rof2SayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -6255,29 +6255,29 @@ namespace RoF2 // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: - rof2TextLink.push_back('\x12'); - rof2TextLink.append(segments[segment_iter]); - rof2TextLink.push_back('\x12'); + rof2SayLink.push_back('\x12'); + rof2SayLink.append(segments[segment_iter]); + rof2SayLink.push_back('\x12'); } else { - rof2TextLink.append(segments[segment_iter]); + rof2SayLink.append(segments[segment_iter]); } } } - static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink) + static inline void RoF2ToServerSayLink(std::string& serverSayLink, const std::string& rof2SayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (rof2TextLink.find('\x12') == std::string::npos)) { - serverTextLink = rof2TextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (rof2SayLink.find('\x12') == std::string::npos)) { + serverSayLink = rof2SayLink; return; } - auto segments = SplitString(rof2TextLink, '\x12'); + auto segments = SplitString(rof2SayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -6287,12 +6287,12 @@ namespace RoF2 // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter]); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter]); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index c516929f4..bd18a13c0 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -5089,6 +5089,23 @@ struct CrystalCountUpdate_Struct /*012*/ uint32 CareerEbonCrystals; }; +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char Augment6[5]; +/*036*/ char IsEvolving[1]; +/*037*/ char EvolveGroup[4]; +/*041*/ char EvolveLevel[2]; +/*043*/ char OrnamentIcon[5]; +/*048*/ char Hash[8]; +/*056*/ +}; + }; /*structs*/ }; /*RoF2*/ diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index db52827b9..d38d5c6bb 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -5006,6 +5006,23 @@ struct MercenaryMerchantRequest_Struct { struct MercenaryMerchantResponse_Struct { /*0000*/ uint32 ResponseType; /*0004*/ +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char Augment6[5]; +/*036*/ char IsEvolving[1]; +/*037*/ char EvolveGroup[4]; +/*041*/ char EvolveLevel[1]; +/*042*/ char OrnamentIcon[5]; +/*047*/ char Hash[8]; +/*055*/ }; }; /*structs*/ diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index ceba224f4..9e1479188 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -53,11 +53,11 @@ namespace SoD static inline uint32 SoDToServerSlot(uint32 sodSlot); static inline uint32 SoDToServerCorpseSlot(uint32 sodCorpseSlot); - // server to client text link converter - static inline void ServerToSoDTextLink(std::string& sodTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToSoDSayLink(std::string& sodSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink); + // client to server say link converter + static inline void SoDToServerSayLink(std::string& serverSayLink, const std::string& sodSayLink); static inline CastingSlot ServerToSoDCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot SoDToServerCastingSlot(CastingSlot slot); @@ -346,7 +346,7 @@ namespace SoD std::string old_message = emu->message; std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -625,7 +625,7 @@ namespace SoD std::string old_message = emu->message; std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -677,7 +677,7 @@ namespace SoD for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToSoDTextLink(new_message_array[i], old_message_array[i]); + ServerToSoDSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -2156,7 +2156,7 @@ namespace SoD std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -2252,7 +2252,7 @@ namespace SoD std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -2953,7 +2953,7 @@ namespace SoD std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; std::string new_message; - SoDToServerTextLink(new_message, old_message); + SoDToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -3067,7 +3067,7 @@ namespace SoD std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - SoDToServerTextLink(new_message, old_message); + SoDToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -3917,19 +3917,19 @@ namespace SoD return (sodCorpseSlot - 1); } - static inline void ServerToSoDTextLink(std::string& sodTextLink, const std::string& serverTextLink) + static inline void ServerToSoDSayLink(std::string& sodSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - sodTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + sodSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - sodTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + sodSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3939,37 +3939,37 @@ namespace SoD // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - sodTextLink.push_back('\x12'); - sodTextLink.append(segments[segment_iter].substr(0, 31)); - sodTextLink.append(segments[segment_iter].substr(36, 5)); + sodSayLink.push_back('\x12'); + sodSayLink.append(segments[segment_iter].substr(0, 31)); + sodSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - sodTextLink.push_back(segments[segment_iter][42]); + sodSayLink.push_back(segments[segment_iter][42]); else - sodTextLink.push_back('F'); + sodSayLink.push_back('F'); - sodTextLink.append(segments[segment_iter].substr(43)); - sodTextLink.push_back('\x12'); + sodSayLink.append(segments[segment_iter].substr(43)); + sodSayLink.push_back('\x12'); } else { - sodTextLink.append(segments[segment_iter]); + sodSayLink.append(segments[segment_iter]); } } } - static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink) + static inline void SoDToServerSayLink(std::string& serverSayLink, const std::string& sodSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (sodTextLink.find('\x12') == std::string::npos)) { - serverTextLink = sodTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (sodSayLink.find('\x12') == std::string::npos)) { + serverSayLink = sodSayLink; return; } - auto segments = SplitString(sodTextLink, '\x12'); + auto segments = SplitString(sodSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3979,16 +3979,16 @@ namespace SoD // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(36)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 1000cb5a3..866418b54 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -4385,6 +4385,22 @@ struct MercenaryAssign_Struct { /*0004*/ uint32 MercUnk01; // /*0008*/ uint32 MercUnk02; // /*0012*/ +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char OrnamentIcon[5]; +/*042*/ char Hash[8]; +/*050*/ }; }; /*structs*/ diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index bee75b6c2..063d99ca4 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -53,11 +53,11 @@ namespace SoF static inline uint32 SoFToServerSlot(uint32 sofSlot); static inline uint32 SoFToServerCorpseSlot(uint32 sofCorpseSlot); - // server to client text link converter - static inline void ServerToSoFTextLink(std::string& sofTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToSoFSayLink(std::string& sofSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink); + // client to server say link converter + static inline void SoFToServerSayLink(std::string& serverSayLink, const std::string& sofSayLink); static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 itemlocation); @@ -328,7 +328,7 @@ namespace SoF std::string old_message = emu->message; std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -613,7 +613,7 @@ namespace SoF std::string old_message = emu->message; std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -665,7 +665,7 @@ namespace SoF for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToSoFTextLink(new_message_array[i], old_message_array[i]); + ServerToSoFSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -1814,7 +1814,7 @@ namespace SoF std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -1882,7 +1882,7 @@ namespace SoF std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -2415,7 +2415,7 @@ namespace SoF std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; std::string new_message; - SoFToServerTextLink(new_message, old_message); + SoFToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -2529,7 +2529,7 @@ namespace SoF std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - SoFToServerTextLink(new_message, old_message); + SoFToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -3316,19 +3316,19 @@ namespace SoF return (sofCorpseSlot - 1); } - static inline void ServerToSoFTextLink(std::string& sofTextLink, const std::string& serverTextLink) + static inline void ServerToSoFSayLink(std::string& sofSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - sofTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + sofSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - sofTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + sofSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3338,37 +3338,37 @@ namespace SoF // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - sofTextLink.push_back('\x12'); - sofTextLink.append(segments[segment_iter].substr(0, 31)); - sofTextLink.append(segments[segment_iter].substr(36, 5)); + sofSayLink.push_back('\x12'); + sofSayLink.append(segments[segment_iter].substr(0, 31)); + sofSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - sofTextLink.push_back(segments[segment_iter][42]); + sofSayLink.push_back(segments[segment_iter][42]); else - sofTextLink.push_back('F'); + sofSayLink.push_back('F'); - sofTextLink.append(segments[segment_iter].substr(43)); - sofTextLink.push_back('\x12'); + sofSayLink.append(segments[segment_iter].substr(43)); + sofSayLink.push_back('\x12'); } else { - sofTextLink.append(segments[segment_iter]); + sofSayLink.append(segments[segment_iter]); } } } - static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink) + static inline void SoFToServerSayLink(std::string& serverSayLink, const std::string& sofSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (sofTextLink.find('\x12') == std::string::npos)) { - serverTextLink = sofTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (sofSayLink.find('\x12') == std::string::npos)) { + serverSayLink = sofSayLink; return; } - auto segments = SplitString(sofTextLink, '\x12'); + auto segments = SplitString(sofSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3378,16 +3378,16 @@ namespace SoF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(36)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index f6298f41d..168dfb123 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -4156,6 +4156,22 @@ struct AltCurrencySellItem_Struct { /*004*/ uint32 slot_id; /*006*/ uint32 charges; /*010*/ uint32 cost; +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char OrnamentIcon[5]; +/*042*/ char Hash[8]; +/*050*/ }; }; /*structs*/ diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 2c010713d..afff0cea2 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -52,11 +52,11 @@ namespace Titanium static inline uint32 TitaniumToServerSlot(int16 titaniumSlot); static inline uint32 TitaniumToServerCorpseSlot(int16 titaniumCorpseSlot); - // server to client text link converter - static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToTitaniumSayLink(std::string& titaniumSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink); + // client to server say link converter + static inline void TitaniumToServerSayLink(std::string& serverSayLink, const std::string& titaniumSayLink); static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 itemlocation); @@ -290,7 +290,7 @@ namespace Titanium std::string old_message = emu->message; std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -532,7 +532,7 @@ namespace Titanium std::string old_message = emu->message; std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -574,7 +574,7 @@ namespace Titanium for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToTitaniumTextLink(new_message_array[i], old_message_array[i]); + ServerToTitaniumSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -1402,7 +1402,7 @@ namespace Titanium std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -1458,7 +1458,7 @@ namespace Titanium std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct) + sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct) + @@ -1819,7 +1819,7 @@ namespace Titanium std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; std::string new_message; - TitaniumToServerTextLink(new_message, old_message); + TitaniumToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -1891,7 +1891,7 @@ namespace Titanium std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - TitaniumToServerTextLink(new_message, old_message); + TitaniumToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -2485,19 +2485,19 @@ namespace Titanium return titaniumCorpseSlot; } - static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink) + static inline void ServerToTitaniumSayLink(std::string& titaniumSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - titaniumTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + titaniumSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - titaniumTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + titaniumSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -2507,37 +2507,37 @@ namespace Titanium // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) // Diff: ^^^^^ ^ ^^^^^ - titaniumTextLink.push_back('\x12'); - titaniumTextLink.append(segments[segment_iter].substr(0, 31)); - titaniumTextLink.append(segments[segment_iter].substr(36, 5)); + titaniumSayLink.push_back('\x12'); + titaniumSayLink.append(segments[segment_iter].substr(0, 31)); + titaniumSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - titaniumTextLink.push_back(segments[segment_iter][42]); + titaniumSayLink.push_back(segments[segment_iter][42]); else - titaniumTextLink.push_back('F'); + titaniumSayLink.push_back('F'); - titaniumTextLink.append(segments[segment_iter].substr(48)); - titaniumTextLink.push_back('\x12'); + titaniumSayLink.append(segments[segment_iter].substr(48)); + titaniumSayLink.push_back('\x12'); } else { - titaniumTextLink.append(segments[segment_iter]); + titaniumSayLink.append(segments[segment_iter]); } } } - static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink) + static inline void TitaniumToServerSayLink(std::string& serverSayLink, const std::string& titaniumSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (titaniumTextLink.find('\x12') == std::string::npos)) { - serverTextLink = titaniumTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (titaniumSayLink.find('\x12') == std::string::npos)) { + serverSayLink = titaniumSayLink; return; } - auto segments = SplitString(titaniumTextLink, '\x12'); + auto segments = SplitString(titaniumSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -2547,18 +2547,18 @@ namespace Titanium // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ ^^^^^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.push_back(segments[segment_iter][36]); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(37)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.push_back(segments[segment_iter][36]); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(37)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 3a2787500..ea698acf0 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3565,6 +3565,21 @@ struct LFGuild_GuildToggle_Struct // char ScrollName; // '0' //}; +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char Hash[8]; +/*045*/ +}; + }; /*structs*/ }; /*Titanium*/ diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index cf0c17176..176e737b7 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -53,11 +53,11 @@ namespace UF static inline uint32 UFToServerSlot(uint32 ufSlot); static inline uint32 UFToServerCorpseSlot(uint32 ufCorpseSlot); - // server to client text link converter - static inline void ServerToUFTextLink(std::string& ufTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToUFSayLink(std::string& ufSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void UFToServerTextLink(std::string& serverTextLink, const std::string& ufTextLink); + // client to server say link converter + static inline void UFToServerSayLink(std::string& serverSayLink, const std::string& ufSayLink); static inline CastingSlot ServerToUFCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot UFToServerCastingSlot(CastingSlot slot); @@ -463,7 +463,7 @@ namespace UF std::string old_message = emu->message; std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; @@ -762,7 +762,7 @@ namespace UF std::string old_message = emu->message; std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -814,7 +814,7 @@ namespace UF for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToUFTextLink(new_message_array[i], old_message_array[i]); + ServerToUFSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -2469,7 +2469,7 @@ namespace UF std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -2539,7 +2539,7 @@ namespace UF std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -3287,7 +3287,7 @@ namespace UF std::string old_message = InBuffer; std::string new_message; - UFToServerTextLink(new_message, old_message); + UFToServerSayLink(new_message, old_message); //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -3421,7 +3421,7 @@ namespace UF std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - UFToServerTextLink(new_message, old_message); + UFToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -4290,19 +4290,19 @@ namespace UF return (ufCorpseSlot - 1); } - static inline void ServerToUFTextLink(std::string& ufTextLink, const std::string& serverTextLink) + static inline void ServerToUFSayLink(std::string& ufSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - ufTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + ufSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - ufTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + ufSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -4312,37 +4312,37 @@ namespace UF // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - ufTextLink.push_back('\x12'); - ufTextLink.append(segments[segment_iter].substr(0, 31)); - ufTextLink.append(segments[segment_iter].substr(36, 5)); + ufSayLink.push_back('\x12'); + ufSayLink.append(segments[segment_iter].substr(0, 31)); + ufSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - ufTextLink.push_back(segments[segment_iter][42]); + ufSayLink.push_back(segments[segment_iter][42]); else - ufTextLink.push_back('F'); + ufSayLink.push_back('F'); - ufTextLink.append(segments[segment_iter].substr(43)); - ufTextLink.push_back('\x12'); + ufSayLink.append(segments[segment_iter].substr(43)); + ufSayLink.push_back('\x12'); } else { - ufTextLink.append(segments[segment_iter]); + ufSayLink.append(segments[segment_iter]); } } } - static inline void UFToServerTextLink(std::string& serverTextLink, const std::string& ufTextLink) + static inline void UFToServerSayLink(std::string& serverSayLink, const std::string& ufSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (ufTextLink.find('\x12') == std::string::npos)) { - serverTextLink = ufTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (ufSayLink.find('\x12') == std::string::npos)) { + serverSayLink = ufSayLink; return; } - auto segments = SplitString(ufTextLink, '\x12'); + auto segments = SplitString(ufSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -4352,16 +4352,16 @@ namespace UF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(36)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 256169d67..8a03144c9 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -4489,6 +4489,22 @@ struct MercenaryAssign_Struct { /*0004*/ uint32 MercUnk01; // /*0008*/ uint32 MercUnk02; // /*0012*/ +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char OrnamentIcon[5]; +/*042*/ char Hash[8]; +/*050*/ }; }; /*structs*/ diff --git a/common/say_link.cpp b/common/say_link.cpp index 923e53407..e2b43c107 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -29,10 +29,10 @@ bool EQEmu::saylink::DegenerateLinkBody(SayLinkBody_Struct& say_link_body_struct, const std::string& say_link_body) { memset(&say_link_body_struct, 0, sizeof(say_link_body_struct)); - if (say_link_body.length() != EQEmu::legacy::TEXT_LINK_BODY_LENGTH) + if (say_link_body.length() != EQEmu::constants::SayLinkBodySize) return false; - say_link_body_struct.unknown_1 = (uint8)strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16); + say_link_body_struct.action_id = (uint8)strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16); say_link_body_struct.item_id = (uint32)strtol(say_link_body.substr(1, 5).c_str(), nullptr, 16); say_link_body_struct.augment_1 = (uint32)strtol(say_link_body.substr(6, 5).c_str(), nullptr, 16); say_link_body_struct.augment_2 = (uint32)strtol(say_link_body.substr(11, 5).c_str(), nullptr, 16); @@ -44,7 +44,7 @@ bool EQEmu::saylink::DegenerateLinkBody(SayLinkBody_Struct& say_link_body_struct say_link_body_struct.evolve_group = (uint32)strtol(say_link_body.substr(37, 4).c_str(), nullptr, 16); say_link_body_struct.evolve_level = (uint8)strtol(say_link_body.substr(41, 2).c_str(), nullptr, 16); say_link_body_struct.ornament_icon = (uint32)strtol(say_link_body.substr(43, 5).c_str(), nullptr, 16); - say_link_body_struct.hash = (int)strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16); + say_link_body_struct.hash = (uint32)strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16); return true; } @@ -53,7 +53,7 @@ bool EQEmu::saylink::GenerateLinkBody(std::string& say_link_body, const SayLinkB { say_link_body = StringFormat( "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", - (0x0F & say_link_body_struct.unknown_1), + (0x0F & say_link_body_struct.action_id), (0x000FFFFF & say_link_body_struct.item_id), (0x000FFFFF & say_link_body_struct.augment_1), (0x000FFFFF & say_link_body_struct.augment_2), @@ -68,7 +68,7 @@ bool EQEmu::saylink::GenerateLinkBody(std::string& say_link_body, const SayLinkB (0xFFFFFFFF & say_link_body_struct.hash) ); - if (say_link_body.length() != EQEmu::legacy::TEXT_LINK_BODY_LENGTH) + if (say_link_body.length() != EQEmu::constants::SayLinkBodySize) return false; return true; @@ -79,7 +79,7 @@ EQEmu::SayLinkEngine::SayLinkEngine() Reset(); } -std::string EQEmu::SayLinkEngine::GenerateLink() +const std::string& EQEmu::SayLinkEngine::GenerateLink() { m_Link.clear(); m_LinkBody.clear(); @@ -88,18 +88,26 @@ std::string EQEmu::SayLinkEngine::GenerateLink() generate_body(); generate_text(); - if ((m_LinkBody.length() == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) && (m_LinkText.length() > 0)) { + if ((m_LinkBody.length() == EQEmu::constants::SayLinkBodySize) && (m_LinkText.length() > 0)) { m_Link.push_back(0x12); m_Link.append(m_LinkBody); m_Link.append(m_LinkText); m_Link.push_back(0x12); } - if ((m_Link.length() == 0) || (m_Link.length() > 250)) { + if ((m_Link.length() == 0) || (m_Link.length() > (EQEmu::constants::SayLinkMaximumSize))) { m_Error = true; m_Link = ""; - Log(Logs::General, Logs::Error, "TextLink::GenerateLink() failed to generate a useable text link (LinkType: %i, Lengths: {link: %u, body: %u, text: %u})", - m_LinkType, m_Link.length(), m_LinkBody.length(), m_LinkText.length()); + Log(Logs::General, Logs::Error, "SayLinkEngine::GenerateLink() failed to generate a useable say link"); + Log(Logs::General, Logs::Error, ">> LinkType: %i, Lengths: {link: %u(%u), body: %u(%u), text: %u(%u)}", + m_LinkType, + m_Link.length(), + EQEmu::constants::SayLinkMaximumSize, + m_LinkBody.length(), + EQEmu::constants::SayLinkBodySize, + m_LinkText.length(), + EQEmu::constants::SayLinkTextSize + ); Log(Logs::General, Logs::Error, ">> LinkBody: %s", m_LinkBody.c_str()); Log(Logs::General, Logs::Error, ">> LinkText: %s", m_LinkText.c_str()); } @@ -113,20 +121,10 @@ void EQEmu::SayLinkEngine::Reset() m_ItemData = nullptr; m_LootData = nullptr; m_ItemInst = nullptr; - m_Proxy_unknown_1 = 0; - m_ProxyItemID = 0; - m_ProxyAugment1ID = 0; - m_ProxyAugment2ID = 0; - m_ProxyAugment3ID = 0; - m_ProxyAugment4ID = 0; - m_ProxyAugment5ID = 0; - m_ProxyAugment6ID = 0; - m_ProxyIsEvolving = 0; - m_ProxyEvolveGroup = 0; - m_ProxyEvolveLevel = 0; - m_ProxyOrnamentIcon = 0; - m_ProxyHash = 0; - m_ProxyText = nullptr; + + memset(&m_LinkBodyStruct, 0, sizeof(SayLinkBody_Struct)); + memset(&m_LinkProxyStruct, 0, sizeof(SayLinkProxy_Struct)); + m_TaskUse = false; m_Link.clear(); m_LinkBody.clear(); @@ -194,32 +192,32 @@ void EQEmu::SayLinkEngine::generate_body() break; } - if (m_Proxy_unknown_1) - m_LinkBodyStruct.unknown_1 = m_Proxy_unknown_1; - if (m_ProxyItemID) - m_LinkBodyStruct.item_id = m_ProxyItemID; - if (m_ProxyAugment1ID) - m_LinkBodyStruct.augment_1 = m_ProxyAugment1ID; - if (m_ProxyAugment2ID) - m_LinkBodyStruct.augment_2 = m_ProxyAugment2ID; - if (m_ProxyAugment3ID) - m_LinkBodyStruct.augment_3 = m_ProxyAugment3ID; - if (m_ProxyAugment4ID) - m_LinkBodyStruct.augment_4 = m_ProxyAugment4ID; - if (m_ProxyAugment5ID) - m_LinkBodyStruct.augment_5 = m_ProxyAugment5ID; - if (m_ProxyAugment6ID) - m_LinkBodyStruct.augment_6 = m_ProxyAugment6ID; - if (m_ProxyIsEvolving) - m_LinkBodyStruct.is_evolving = m_ProxyIsEvolving; - if (m_ProxyEvolveGroup) - m_LinkBodyStruct.evolve_group = m_ProxyEvolveGroup; - if (m_ProxyEvolveLevel) - m_LinkBodyStruct.evolve_level = m_ProxyEvolveLevel; - if (m_ProxyOrnamentIcon) - m_LinkBodyStruct.ornament_icon = m_ProxyOrnamentIcon; - if (m_ProxyHash) - m_LinkBodyStruct.hash = m_ProxyHash; + if (m_LinkProxyStruct.action_id) + m_LinkBodyStruct.action_id = m_LinkProxyStruct.action_id; + if (m_LinkProxyStruct.item_id) + m_LinkBodyStruct.item_id = m_LinkProxyStruct.item_id; + if (m_LinkProxyStruct.augment_1) + m_LinkBodyStruct.augment_1 = m_LinkProxyStruct.augment_1; + if (m_LinkProxyStruct.augment_2) + m_LinkBodyStruct.augment_2 = m_LinkProxyStruct.augment_2; + if (m_LinkProxyStruct.augment_3) + m_LinkBodyStruct.augment_3 = m_LinkProxyStruct.augment_3; + if (m_LinkProxyStruct.augment_4) + m_LinkBodyStruct.augment_4 = m_LinkProxyStruct.augment_4; + if (m_LinkProxyStruct.augment_5) + m_LinkBodyStruct.augment_5 = m_LinkProxyStruct.augment_5; + if (m_LinkProxyStruct.augment_6) + m_LinkBodyStruct.augment_6 = m_LinkProxyStruct.augment_6; + if (m_LinkProxyStruct.is_evolving) + m_LinkBodyStruct.is_evolving = m_LinkProxyStruct.is_evolving; + if (m_LinkProxyStruct.evolve_group) + m_LinkBodyStruct.evolve_group = m_LinkProxyStruct.evolve_group; + if (m_LinkProxyStruct.evolve_level) + m_LinkBodyStruct.evolve_level = m_LinkProxyStruct.evolve_level; + if (m_LinkProxyStruct.ornament_icon) + m_LinkBodyStruct.ornament_icon = m_LinkProxyStruct.ornament_icon; + if (m_LinkProxyStruct.hash) + m_LinkBodyStruct.hash = m_LinkProxyStruct.hash; if (m_TaskUse) @@ -227,7 +225,7 @@ void EQEmu::SayLinkEngine::generate_body() m_LinkBody = StringFormat( "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", - (0x0F & m_LinkBodyStruct.unknown_1), + (0x0F & m_LinkBodyStruct.action_id), (0x000FFFFF & m_LinkBodyStruct.item_id), (0x000FFFFF & m_LinkBodyStruct.augment_1), (0x000FFFFF & m_LinkBodyStruct.augment_2), @@ -245,8 +243,8 @@ void EQEmu::SayLinkEngine::generate_body() void EQEmu::SayLinkEngine::generate_text() { - if (m_ProxyText != nullptr) { - m_LinkText = m_ProxyText; + if (m_LinkProxyStruct.text != nullptr) { + m_LinkText = m_LinkProxyStruct.text; return; } diff --git a/common/say_link.h b/common/say_link.h index 6bc3e5535..55e3a8237 100644 --- a/common/say_link.h +++ b/common/say_link.h @@ -47,7 +47,7 @@ namespace EQEmu } /*saylink*/ struct SayLinkBody_Struct { - uint8 unknown_1; /* %1X */ + uint8 action_id; /* %1X */ uint32 item_id; /* %05X */ uint32 augment_1; /* %05X */ uint32 augment_2; /* %05X */ @@ -56,13 +56,18 @@ namespace EQEmu uint32 augment_5; /* %05X */ uint32 augment_6; /* %05X */ uint8 is_evolving; /* %1X */ - uint32 evolve_group; /* %05X */ + uint32 evolve_group; /* %04X */ uint8 evolve_level; /* %02X */ uint32 ornament_icon; /* %05X */ - int hash; /* %08X */ + uint32 hash; /* %08X */ + }; + + struct SayLinkProxy_Struct : SayLinkBody_Struct { + const char* text; }; class SayLinkEngine { + // TODO: consider methods for direct 'saylink' assignments public: SayLinkEngine(); @@ -72,29 +77,29 @@ namespace EQEmu void SetItemInst(const ItemInstance* item_inst) { m_ItemInst = item_inst; } // mainly for saylinks..but, not limited to - void SetProxyUnknown1(uint8 proxy_unknown_1) { m_Proxy_unknown_1 = proxy_unknown_1; } - void SetProxyItemID(uint32 proxy_item_id) { m_ProxyItemID = proxy_item_id; } - void SetProxyAugment1ID(uint32 proxy_augment_id) { m_ProxyAugment1ID = proxy_augment_id; } - void SetProxyAugment2ID(uint32 proxy_augment_id) { m_ProxyAugment2ID = proxy_augment_id; } - void SetProxyAugment3ID(uint32 proxy_augment_id) { m_ProxyAugment3ID = proxy_augment_id; } - void SetProxyAugment4ID(uint32 proxy_augment_id) { m_ProxyAugment4ID = proxy_augment_id; } - void SetProxyAugment5ID(uint32 proxy_augment_id) { m_ProxyAugment5ID = proxy_augment_id; } - void SetProxyAugment6ID(uint32 proxy_augment_id) { m_ProxyAugment6ID = proxy_augment_id; } - void SetProxyIsEvolving(uint8 proxy_is_evolving) { m_ProxyIsEvolving = proxy_is_evolving; } - void SetProxyEvolveGroup(uint32 proxy_evolve_group) { m_ProxyEvolveGroup = proxy_evolve_group; } - void SetProxyEvolveLevel(uint8 proxy_evolve_level) { m_ProxyEvolveLevel = proxy_evolve_level; } - void SetProxyOrnamentIcon(uint32 proxy_ornament_icon) { m_ProxyOrnamentIcon = proxy_ornament_icon; } - void SetProxyHash(int proxy_hash) { m_ProxyHash = proxy_hash; } + void SetProxyActionID(uint8 proxy_action_id) { m_LinkProxyStruct.action_id = proxy_action_id; } // should always be '0' + void SetProxyItemID(uint32 proxy_item_id) { m_LinkProxyStruct.item_id = proxy_item_id; } + void SetProxyAugment1ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_1 = proxy_augment_id; } + void SetProxyAugment2ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_2 = proxy_augment_id; } + void SetProxyAugment3ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_3 = proxy_augment_id; } + void SetProxyAugment4ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_4 = proxy_augment_id; } + void SetProxyAugment5ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_5 = proxy_augment_id; } + void SetProxyAugment6ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_6 = proxy_augment_id; } + void SetProxyIsEvolving(uint8 proxy_is_evolving) { m_LinkProxyStruct.is_evolving = proxy_is_evolving; } + void SetProxyEvolveGroup(uint32 proxy_evolve_group) { m_LinkProxyStruct.evolve_group = proxy_evolve_group; } + void SetProxyEvolveLevel(uint8 proxy_evolve_level) { m_LinkProxyStruct.evolve_level = proxy_evolve_level; } + void SetProxyOrnamentIcon(uint32 proxy_ornament_icon) { m_LinkProxyStruct.ornament_icon = proxy_ornament_icon; } + void SetProxyHash(uint32 proxy_hash) { m_LinkProxyStruct.hash = proxy_hash; } - void SetProxyText(const char* proxy_text) { m_ProxyText = proxy_text; } // overrides standard text use + void SetProxyText(const char* proxy_text) { m_LinkProxyStruct.text = proxy_text; } // overrides standard text use void SetTaskUse() { m_TaskUse = true; } - std::string GenerateLink(); + const std::string& GenerateLink(); bool LinkError() { return m_Error; } - std::string Link() { return m_Link; } // contains full string format: '/12x' '' '' '/12x' - std::string LinkBody() { return m_LinkBody; } // contains string format: '' - std::string LinkText() { return m_LinkText; } // contains string format: '' + const std::string& Link() { return m_Link; } // contains full string format: '\x12' '' '' '\x12' + const std::string& LinkBody() { return m_LinkBody; } // contains string format: '' + const std::string& LinkText() { return m_LinkText; } // contains string format: '' void Reset(); @@ -106,23 +111,9 @@ namespace EQEmu const ItemData* m_ItemData; const ServerLootItem_Struct* m_LootData; const ItemInstance* m_ItemInst; - - uint8 m_Proxy_unknown_1; - uint32 m_ProxyItemID; - uint32 m_ProxyAugment1ID; - uint32 m_ProxyAugment2ID; - uint32 m_ProxyAugment3ID; - uint32 m_ProxyAugment4ID; - uint32 m_ProxyAugment5ID; - uint32 m_ProxyAugment6ID; - uint8 m_ProxyIsEvolving; - uint32 m_ProxyEvolveGroup; - uint8 m_ProxyEvolveLevel; - uint32 m_ProxyOrnamentIcon; - int m_ProxyHash; - const char* m_ProxyText; - bool m_TaskUse; SayLinkBody_Struct m_LinkBodyStruct; + SayLinkProxy_Struct m_LinkProxyStruct; + bool m_TaskUse; std::string m_Link; std::string m_LinkBody; std::string m_LinkText; diff --git a/zone/bot.cpp b/zone/bot.cpp index 95ecf7885..8751d40f7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8894,6 +8894,8 @@ bool Bot::DyeArmor(int16 slot_id, uint32 rgb, bool all_flag, bool save_flag) std::string Bot::CreateSayLink(Client* c, const char* message, const char* name) { + // TODO: review + int saylink_size = strlen(message); char* escaped_string = new char[saylink_size * 2]; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index cd5e727d0..a91624df1 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -7085,7 +7085,6 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) const EQEmu::ItemData* item = nullptr; bool is2Hweapon = false; - std::string item_link; EQEmu::SayLinkEngine linker; linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); @@ -7106,8 +7105,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) } linker.SetItemInst(inst); - item_link = linker.GenerateLink(); - c->Message(m_message, "Using %s in my %s (slot %i)", item_link.c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::inventory::slotPowerSource : i)); + c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::inventory::slotPowerSource : i)); ++inventory_count; } @@ -7250,8 +7248,8 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep) std::string window_text; //std::string item_link; - //Client::TextLink linker; - //linker.SetLinkType(linker.linkItemInst); + //EQEmu::SayLinkEngine linker; + //linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); for (int i = EQEmu::legacy::EQUIPMENT_BEGIN; i <= (EQEmu::legacy::EQUIPMENT_END + 1); ++i) { const EQEmu::ItemData* item = nullptr; diff --git a/zone/client.cpp b/zone/client.cpp index 69c43ef5a..a62b94c43 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7907,7 +7907,7 @@ void Client::GarbleMessage(char *message, uint8 variance) for (size_t i = 0; i < strlen(message); i++) { // Client expects hex values inside of a text link body if (message[i] == delimiter) { - if (!(delimiter_count & 1)) { i += EQEmu::legacy::TEXT_LINK_BODY_LENGTH; } + if (!(delimiter_count & 1)) { i += EQEmu::constants::SayLinkBodySize; } ++delimiter_count; continue; } diff --git a/zone/command.cpp b/zone/command.cpp index dcfecce7a..1fee0e26d 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2549,7 +2549,7 @@ void command_peekinv(Client *c, const Seperator *sep) const EQEmu::ItemInstance* inst_main = nullptr; const EQEmu::ItemInstance* inst_sub = nullptr; const EQEmu::ItemData* item_data = nullptr; - std::string item_link; + EQEmu::SayLinkEngine linker; linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); @@ -2561,10 +2561,8 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } if ((scopeWhere & peekWorn) && (targetClient->ClientVersion() >= EQEmu::versions::ClientVersion::SoF)) { @@ -2572,10 +2570,8 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", - EQEmu::inventory::slotPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + EQEmu::inventory::slotPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } // inv @@ -2584,20 +2580,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2606,10 +2598,8 @@ void command_peekinv(Client *c, const Seperator *sep) if (targetClient->GetInv().CursorEmpty()) { linker.SetItemInst(nullptr); - item_link = linker.GenerateLink(); - c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i", - EQEmu::inventory::slotCursor, 0, item_link.c_str(), 0); + EQEmu::inventory::slotCursor, 0, linker.GenerateLink().c_str(), 0); } else { int cursorDepth = 0; @@ -2618,20 +2608,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", - EQEmu::inventory::slotCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + EQEmu::inventory::slotCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; (cursorDepth == 0) && inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(EQEmu::inventory::slotCursor, indexSub), EQEmu::inventory::slotCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(EQEmu::inventory::slotCursor, indexSub), EQEmu::inventory::slotCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } } @@ -2643,10 +2629,8 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } // bank @@ -2655,20 +2639,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2677,20 +2657,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2700,20 +2676,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2732,20 +2704,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i", - (EQEmu::legacy::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + (EQEmu::legacy::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsType(EQEmu::item::ItemClassBag) && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } } @@ -4400,9 +4368,7 @@ void command_iteminfo(Client *c, const Seperator *sep) linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); linker.SetItemInst(inst); - auto item_link = linker.GenerateLink(); - - c->Message(0, "*** Item Info for [%s] ***", item_link.c_str()); + c->Message(0, "*** Item Info for [%s] ***", linker.GenerateLink().c_str()); c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice); @@ -5546,9 +5512,9 @@ void command_summonitem(Client *c, const Seperator *sep) std::string cmd_msg = sep->msg; size_t link_open = cmd_msg.find('\x12'); size_t link_close = cmd_msg.find_last_of('\x12'); - if (link_open != link_close && (cmd_msg.length() - link_open) > EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { + if (link_open != link_close && (cmd_msg.length() - link_open) > EQEmu::constants::SayLinkBodySize) { EQEmu::SayLinkBody_Struct link_body; - EQEmu::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQEmu::legacy::TEXT_LINK_BODY_LENGTH)); + EQEmu::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQEmu::constants::SayLinkBodySize)); itemid = link_body.item_id; } else if (!sep->IsNumber(1)) { @@ -5657,7 +5623,6 @@ void command_itemsearch(Client *c, const Seperator *sep) const char *search_criteria=sep->argplus[1]; const EQEmu::ItemData* item = nullptr; - std::string item_link; EQEmu::SayLinkEngine linker; linker.SetLinkType(EQEmu::saylink::SayLinkItemData); @@ -5666,9 +5631,7 @@ void command_itemsearch(Client *c, const Seperator *sep) if (item) { linker.SetItemData(item); - item_link = linker.GenerateLink(); - - c->Message(0, "%u: %s", item->ID, item_link.c_str()); + c->Message(0, "%u: %s", item->ID, linker.GenerateLink().c_str()); } else { c->Message(0, "Item #%s not found", search_criteria); @@ -5691,9 +5654,7 @@ void command_itemsearch(Client *c, const Seperator *sep) if (pdest != nullptr) { linker.SetItemData(item); - item_link = linker.GenerateLink(); - - c->Message(0, "%u: %s", item->ID, item_link.c_str()); + c->Message(0, "%u: %s", item->ID, linker.GenerateLink().c_str()); ++count; } diff --git a/zone/corpse.cpp b/zone/corpse.cpp index a8f3e7ba1..63031ec45 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1253,20 +1253,20 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); linker.SetItemInst(inst); - auto item_link = linker.GenerateLink(); + linker.GenerateLink(); - client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str()); + client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, linker.Link().c_str()); if (!IsPlayerCorpse()) { Group *g = client->GetGroup(); if (g != nullptr) { g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, - client->GetName(), item_link.c_str()); + client->GetName(), linker.Link().c_str()); } else { Raid *r = client->GetRaid(); if (r != nullptr) { r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, - client->GetName(), item_link.c_str()); + client->GetName(), linker.Link().c_str()); } } } diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 49e8e83b5..17cfc1aa1 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1326,7 +1326,7 @@ int Client::GetItemLinkHash(const EQEmu::ItemInstance* inst) { return hash; } -// This appears to still be in use... The core of this should be incorporated into class Client::TextLink +// This appears to still be in use... The core of this should be incorporated into class EQEmu::SayLinkEngine void Client::SendItemLink(const EQEmu::ItemInstance* inst, bool send_to_all) { /* diff --git a/zone/npc.cpp b/zone/npc.cpp index 9f50e7ff9..0fcc1e17d 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -583,9 +583,7 @@ void NPC::QueryLoot(Client* to) linker.SetLinkType(EQEmu::saylink::SayLinkLootItem); linker.SetLootData(*cur); - auto item_link = linker.GenerateLink(); - - to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", item_link.c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level); + to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", linker.GenerateLink().c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level); } to->Message(0, "%i items on %s.", x, GetName()); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 6c0bdfe0a..27e76813d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1319,9 +1319,7 @@ void QuestManager::itemlink(int item_id) { linker.SetLinkType(EQEmu::saylink::SayLinkItemData); linker.SetItemData(item); - auto item_link = linker.GenerateLink(); - - initiator->Message(0, "%s tells you, %s", owner->GetCleanName(), item_link.c_str()); + initiator->Message(0, "%s tells you, %s", owner->GetCleanName(), linker.GenerateLink().c_str()); } } @@ -2549,9 +2547,8 @@ const char* QuestManager::varlink(char* perltext, int item_id) { linker.SetLinkType(EQEmu::saylink::SayLinkItemData); linker.SetItemData(item); - auto item_link = linker.GenerateLink(); - strcpy(perltext, item_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() - + strcpy(perltext, linker.GenerateLink().c_str()); + return perltext; } @@ -2773,8 +2770,7 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam linker.SetProxyAugment1ID(sayid); linker.SetProxyText(LinkName); - auto say_link = linker.GenerateLink(); - strcpy(Phrase, say_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() + strcpy(Phrase, linker.GenerateLink().c_str()); return Phrase; } diff --git a/zone/tasks.cpp b/zone/tasks.cpp index bcddfcf0a..06e3b4c59 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2804,8 +2804,7 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN if (strlen(Tasks[TaskID]->Reward) != 0) linker.SetProxyText(Tasks[TaskID]->Reward); - auto reward_link = linker.GenerateLink(); - reward_text.append(reward_link); + reward_text.append(linker.GenerateLink()); } else { reward_text.append(Tasks[TaskID]->Reward); From 6c2a8edea6e131d937504fc3b0de609cd937f843 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 25 Feb 2018 21:40:45 -0500 Subject: [PATCH 14/59] Added ClientVersion request system to UCS server (needed to fix saylinks) --- common/emu_versions.h | 4 +- common/ruletypes.h | 2 + common/servertalk.h | 11 ++++++ ucs/clientlist.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++ ucs/clientlist.h | 18 +++++++++ ucs/ucs.cpp | 5 +++ ucs/worldserver.cpp | 18 +++++++++ ucs/worldserver.h | 2 + world/client.cpp | 8 ++++ world/client.h | 1 + world/ucs.cpp | 8 ++++ world/zoneserver.cpp | 1 + zone/worldserver.cpp | 14 +++++++ 13 files changed, 175 insertions(+), 2 deletions(-) diff --git a/common/emu_versions.h b/common/emu_versions.h index 9d9e1f580..9ab5becde 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -28,7 +28,7 @@ namespace EQEmu { namespace versions { - enum class ClientVersion { + enum class ClientVersion : uint32 { Unknown = 0, Client62, // Build: 'Aug 4 2005 15:40:59' Titanium, // Build: 'Oct 31 2005 10:33:37' @@ -72,7 +72,7 @@ namespace EQEmu uint32 ConvertClientVersionToExpansion(ClientVersion client_version); - enum class MobVersion { + enum class MobVersion : uint32 { Unknown = 0, Client62, Titanium, diff --git a/common/ruletypes.h b/common/ruletypes.h index b6acffc3c..ea220a511 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -613,6 +613,8 @@ RULE_INT(Chat, IntervalDurationMS, 60000) RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000) RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit RULE_INT(Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. +RULE_INT(Chat, ExpireClientVersionRequests, 3) // time in seconds to keep current cv requests active +RULE_INT(Chat, ExpireClientVersionReplies, 30) // time in seconds to keep current cv replies active RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/common/servertalk.h b/common/servertalk.h index f5965cd74..36e973195 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -190,6 +190,8 @@ #define ServerOP_ReloadLogs 0x4010 #define ServerOP_ReloadPerlExportSettings 0x4011 #define ServerOP_CZSetEntityVariableByClientName 0x4012 +#define ServerOP_UCSClientVersionRequest 0x4013 +#define ServerOP_UCSClientVersionReply 0x4014 /* Query Server OP Codes */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 @@ -1278,6 +1280,15 @@ struct ServerRequestTellQueue_Struct { char name[64]; }; +struct UCSClientVersionRequest_Struct { + uint32 character_id; +}; + +struct UCSClientVersionReply_Struct { + uint32 character_id; + EQEmu::versions::ClientVersion client_version; +}; + #pragma pack() #endif diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index 998410ec1..751cf4089 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -512,7 +512,9 @@ Client::Client(std::shared_ptr eqs) { AccountGrabUpdateTimer = new Timer(60000); //check every minute GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); + RawConnectionType = '\0'; TypeOfConnection = ConnectionTypeUnknown; + ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; UnderfootOrLater = false; } @@ -643,6 +645,10 @@ void Clientlist::Process() database.GetAccountStatus((*it)); + // give world packet a chance to arrive and be processed + if ((*it)->GetCharID()) + ClientVersionRequestQueue[(*it)->GetCharID()] = (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionRequests) * 1000)); + if ((*it)->GetConnectionType() == ConnectionTypeCombined) (*it)->SendFriends(); @@ -681,8 +687,35 @@ void Clientlist::Process() it = ClientChatConnections.erase(it); continue; } + + // initiate request if we don't already have a reply from 'world enter' (ucs crash recovery protocol) + if ((*it)->GetClientVersion() == EQEmu::versions::ClientVersion::Unknown) { + if (!CheckForClientVersionReply((*it))) + RequestClientVersion((*it)->GetCharID()); + } + ++it; } + + // delete expired replies + auto repiter = ClientVersionReplyQueue.begin(); + while (repiter != ClientVersionReplyQueue.end()) { + if ((*repiter).second.second <= Timer::GetCurrentTime()) { + repiter = ClientVersionReplyQueue.erase(repiter); + continue; + } + ++repiter; + } + + // delete expired requests + auto reqiter = ClientVersionRequestQueue.begin(); + while (reqiter != ClientVersionRequestQueue.end()) { + if ((*reqiter).second <= Timer::GetCurrentTime()) { + reqiter = ClientVersionRequestQueue.erase(reqiter); + continue; + } + ++reqiter; + } } void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) @@ -840,6 +873,55 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) } } +void Clientlist::RequestClientVersion(uint32 character_id) { + if (!character_id) + return; + + if (ClientVersionRequestQueue.find(character_id) != ClientVersionRequestQueue.end()) { + if (ClientVersionRequestQueue[character_id] > Timer::GetCurrentTime()) + return; + } + + if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { + Log(Logs::Detail, Logs::UCS_Server, "Requesting ClientVersion reply for character id: %u", + character_id); + } + ClientVersionRequestIDs.push_back(character_id); + ClientVersionRequestQueue[character_id] = (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionRequests) * 1000)); +} + +bool Clientlist::QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version) { + if (!character_id) + return true; + if (client_version == EQEmu::versions::ClientVersion::Unknown) + return false; + + if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { + Log(Logs::Detail, Logs::UCS_Server, "Queueing ClientVersion %u reply for character id: %u", + static_cast(client_version), character_id); + } + ClientVersionReplyQueue[character_id] = cvt_pair(client_version, (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionReplies) * 1000))); + ClientVersionRequestQueue.erase(character_id); + + return true; +} + +bool Clientlist::CheckForClientVersionReply(Client* c) { + if (!c) + return true; + if (ClientVersionReplyQueue.find(c->GetCharID()) == ClientVersionReplyQueue.end()) + return false; + + if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { + Log(Logs::General, Logs::UCS_Server, "Registering ClientVersion %s for stream %s:%u", + EQEmu::versions::ClientVersionName(ClientVersionReplyQueue[c->GetCharID()].first), c->ClientStream->GetRemoteAddr().c_str(), c->ClientStream->GetRemotePort()); + } + c->SetClientVersion(ClientVersionReplyQueue[c->GetCharID()].first); + ClientVersionReplyQueue.erase(c->GetCharID()); + + return true; +} + void Clientlist::CloseAllConnections() { @@ -2132,6 +2214,8 @@ void Client::AccountUpdate() void Client::SetConnectionType(char c) { + RawConnectionType = c; + switch (c) { case 'S': @@ -2161,6 +2245,7 @@ void Client::SetConnectionType(char c) { } default: { + RawConnectionType = '\0'; TypeOfConnection = ConnectionTypeUnknown; Log(Logs::Detail, Logs::UCS_Server, "Connection type is unknown."); } diff --git a/ucs/clientlist.h b/ucs/clientlist.h index 9b72df51a..f84bdc8a2 100644 --- a/ucs/clientlist.h +++ b/ucs/clientlist.h @@ -139,8 +139,13 @@ public: std::string MailBoxName(); int GetMailBoxNumber() { return CurrentMailBox; } int GetMailBoxNumber(std::string CharacterName); + + char GetRawConnectionType() { return RawConnectionType; } void SetConnectionType(char c); ConnectionType GetConnectionType() { return TypeOfConnection; } + void SetClientVersion(EQEmu::versions::ClientVersion client_version) { ClientVersion_ = client_version; } + EQEmu::versions::ClientVersion GetClientVersion() { return ClientVersion_; } + inline bool IsMailConnection() { return (TypeOfConnection == ConnectionTypeMail) || (TypeOfConnection == ConnectionTypeCombined); } void SendNotification(int MailBoxNumber, std::string From, std::string Subject, int MessageID); void ChangeMailBox(int NewMailBox); @@ -167,7 +172,10 @@ private: Timer *GlobalChatLimiterTimer; //60 seconds int AttemptedMessages; bool ForceDisconnect; + + char RawConnectionType; ConnectionType TypeOfConnection; + EQEmu::versions::ClientVersion ClientVersion_; bool UnderfootOrLater; }; @@ -182,12 +190,22 @@ public: Client *IsCharacterOnline(std::string CharacterName); void ProcessOPMailCommand(Client *c, std::string CommandString); + std::list ClientVersionRequestIDs; + + void RequestClientVersion(uint32 character_id); + bool QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version); + bool CheckForClientVersionReply(Client* c); + private: + typedef std::pair cvt_pair; EQ::Net::EQStreamManager *chatsf; std::list ClientChatConnections; + std::map ClientVersionRequestQueue; + std::map ClientVersionReplyQueue; + OpcodeManager *ChatOpMgr; }; diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index b9e7f41b4..1fbfec995 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -140,12 +140,17 @@ int main() { worldserver = new WorldServer; + // now that we can send packets to world, see if there's a + // broadcast opcode that tells the client to relog into ucs + while(RunLoops) { Timer::SetCurrentTime(); g_Clientlist->Process(); + worldserver->ProcessClientVersionRequests(g_Clientlist->ClientVersionRequestIDs); + if(ChannelListProcessTimer.Check()) ChannelList->Process(); diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index 141211032..b5251b64c 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -114,5 +114,23 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) std::string()); break; } + + case ServerOP_UCSClientVersionReply: + { + UCSClientVersionReply_Struct* cvr = (UCSClientVersionReply_Struct*)pack->pBuffer; + g_Clientlist->QueueClientVersionReply(cvr->character_id, cvr->client_version); + break; + } } } + +void WorldServer::ProcessClientVersionRequests(std::list& id_list) { + UCSClientVersionRequest_Struct cvr; + EQ::Net::DynamicPacket dp_cvr; + for (auto iter : id_list) { + cvr.character_id = iter; + dp_cvr.PutData(0, &cvr, sizeof(cvr)); + m_connection->Send(ServerOP_UCSClientVersionRequest, dp_cvr); + } + id_list.clear(); +} diff --git a/ucs/worldserver.h b/ucs/worldserver.h index aad68e085..53f52823a 100644 --- a/ucs/worldserver.h +++ b/ucs/worldserver.h @@ -29,6 +29,8 @@ public: ~WorldServer(); void ProcessMessage(uint16 opcode, EQ::Net::Packet &); + void ProcessClientVersionRequests(std::list& id_list); + private: std::unique_ptr m_connection; diff --git a/world/client.cpp b/world/client.cpp index aaf7d4c9c..d904e1d2d 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1215,6 +1215,14 @@ void Client::EnterWorld(bool TryBootup) { wtz->response = 0; zone_server->SendPacket(pack); delete pack; + + UCSClientVersionReply_Struct cvr; + cvr.character_id = GetCharID(); + cvr.client_version = GetClientVersion(); + EQ::Net::DynamicPacket dp_cvr; + dp_cvr.PutData(0, &cvr, sizeof(cvr)); + zone_server->HandleMessage(ServerOP_UCSClientVersionReply, dp_cvr); + } else { // if they havent seen character select screen, we can assume this is a zone // to zone movement, which should be preauthorized before they leave the previous zone diff --git a/world/client.h b/world/client.h index 88dc27c57..d36dc692d 100644 --- a/world/client.h +++ b/world/client.h @@ -68,6 +68,7 @@ public: inline const char* GetLSKey() { if (cle) { return cle->GetLSKey(); } return "NOKEY"; } inline uint32 GetCharID() { return charid; } inline const char* GetCharName() { return char_name; } + inline EQEmu::versions::ClientVersion GetClientVersion() { return m_ClientVersion; } inline ClientListEntry* GetCLE() { return cle; } inline void SetCLE(ClientListEntry* iCLE) { cle = iCLE; } private: diff --git a/world/ucs.cpp b/world/ucs.cpp index b745005d4..dfa8e116a 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -2,11 +2,14 @@ #include "../common/eqemu_logsys.h" #include "ucs.h" #include "world_config.h" +#include "zonelist.h" #include "../common/misc_functions.h" #include "../common/md5.h" #include "../common/packet_dump.h" +extern ZSList zoneserver_list; + UCSConnection::UCSConnection() { Stream = 0; @@ -49,6 +52,11 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) Log(Logs::Detail, Logs::UCS_Server, "Got authentication from UCS when they are already authenticated."); break; } + case ServerOP_UCSClientVersionRequest: + { + zoneserver_list.SendPacket(pack); + break; + } default: { Log(Logs::Detail, Logs::UCS_Server, "Unknown ServerOPcode from UCS 0x%04x, size %d", opcode, pack->size); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 4de771701..061ee6b72 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1262,6 +1262,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } + case ServerOP_UCSClientVersionReply: case ServerOP_UCSMailMessage: { UCSLink.SendPacket(pack); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 6417b9c1d..1f771d131 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1813,6 +1813,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_UCSClientVersionRequest: + { + UCSClientVersionRequest_Struct* cvreq = (UCSClientVersionRequest_Struct*)pack->pBuffer; + Client* c = entity_list.GetClientByCharID(cvreq->character_id); + if (c) { + UCSClientVersionReply_Struct cvrep; + cvrep.character_id = c->CharacterID(); + cvrep.client_version = c->ClientVersion(); + EQ::Net::DynamicPacket dp_cvrep; + dp_cvrep.PutData(0, &cvrep, sizeof(cvrep)); + worldserver.m_connection->Send(ServerOP_UCSClientVersionReply, dp_cvrep); + } + break; + } case ServerOP_CZSetEntityVariableByNPCTypeID: { CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer; From 008b17aaf261b5920cba6b98d265578a2e364d1e Mon Sep 17 00:00:00 2001 From: Xackery Date: Mon, 26 Feb 2018 09:33:15 -0800 Subject: [PATCH 15/59] added minimum level to pvp flag. --- common/ruletypes.h | 1 + zone/client.cpp | 2 +- zone/exp.cpp | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index b6acffc3c..d80996184 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -234,6 +234,7 @@ RULE_INT(World, TitaniumStartZoneID, -1) //Sets the Starting Zone for Titanium C RULE_INT(World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS. RULE_BOOL(World, UseClientBasedExpansionSettings, true) // if true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using RULE_INT(World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules. +RULE_INT(World, PVPMinLevel, 0) // minimum level to pvp RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) diff --git a/zone/client.cpp b/zone/client.cpp index 69c43ef5a..b507bbb99 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -254,7 +254,7 @@ Client::Client(EQStreamInterface* ieqs) mercSlot = 0; InitializeMercInfo(); SetMerc(0); - + if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true, false); logging_enabled = CLIENT_DEFAULT_LOGGING_ENABLED; //for good measure: diff --git a/zone/exp.cpp b/zone/exp.cpp index be9d01c13..2902610a8 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -831,6 +831,8 @@ void Client::SetLevel(uint8 set_level, bool command) SetHP(CalcMaxHP()); // Why not, lets give them a free heal } + if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true); + DoTributeUpdate(); SendHPUpdate(); SetMana(CalcMaxMana()); From 464c1cb584c171418a2d866d2785b6e7b38a71e5 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 26 Feb 2018 13:53:33 -0500 Subject: [PATCH 16/59] quest::npcfeature() could not set all features --- zone/questmgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index c77e96979..1c15c54dc 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1981,8 +1981,8 @@ void QuestManager::npcfeature(char *feature, int setting) QuestManagerCurrentQuestVars(); uint16 Race = owner->GetRace(); uint8 Gender = owner->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; + uint8 Texture = owner->GetTexture(); + uint8 HelmTexture = owner->GetHelmTexture(); uint8 HairColor = owner->GetHairColor(); uint8 BeardColor = owner->GetBeardColor(); uint8 EyeColor1 = owner->GetEyeColor1(); From e547a1e778fe548a2c851ff6de22d820e4406d29 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 26 Feb 2018 20:02:27 -0500 Subject: [PATCH 17/59] Added 'server ready' broadcast to UCS server so clients will reconnect after crash --- common/ruletypes.h | 1 + common/servertalk.h | 6 ++++++ common/shareddb.cpp | 17 +++++++++++++++ common/shareddb.h | 1 + ucs/ucs.cpp | 6 ++++-- ucs/worldserver.cpp | 36 +++++++++++++++++++++++++++++++ ucs/worldserver.h | 5 +++++ world/ucs.cpp | 1 + world/worlddb.cpp | 17 --------------- world/worlddb.h | 1 - zone/worldserver.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 122 insertions(+), 20 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index ea220a511..ed51f4474 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -615,6 +615,7 @@ RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able t RULE_INT(Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. RULE_INT(Chat, ExpireClientVersionRequests, 3) // time in seconds to keep current cv requests active RULE_INT(Chat, ExpireClientVersionReplies, 30) // time in seconds to keep current cv replies active +RULE_INT(Chat, UCSBroadcastServerReadyDelay, 60) // time in seconds to delay broadcast `server ready` after start-up RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/common/servertalk.h b/common/servertalk.h index 36e973195..82f39319f 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -192,6 +192,7 @@ #define ServerOP_CZSetEntityVariableByClientName 0x4012 #define ServerOP_UCSClientVersionRequest 0x4013 #define ServerOP_UCSClientVersionReply 0x4014 +#define ServerOP_UCSBroadcastServerReady 0x4015 /* Query Server OP Codes */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 @@ -1289,6 +1290,11 @@ struct UCSClientVersionReply_Struct { EQEmu::versions::ClientVersion client_version; }; +struct UCSBroadcastServerReady_Struct { + char chat_prefix[128]; + char mail_prefix[128]; +}; + #pragma pack() #endif diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 09c936425..4a82c32da 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -110,6 +110,23 @@ uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { return EntitledTime; } +void SharedDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) +{ + char MailKeyString[17]; + + if (RuleB(Chat, EnableMailKeyIPVerification) == true) + sprintf(MailKeyString, "%08X%08X", IPAddress, MailKey); + else + sprintf(MailKeyString, "%08X", MailKey); + + std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", + MailKeyString, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + Log(Logs::General, Logs::Error, "SharedDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); + +} + bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) { // Delete cursor items diff --git a/common/shareddb.h b/common/shareddb.h index d3d8020a1..171c2b8c6 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -72,6 +72,7 @@ class SharedDatabase : public Database void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message); bool GetCommandSettings(std::map>> &command_settings); uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); + void SetMailKey(int CharID, int IPAddress, int MailKey); /* Character InventoryProfile diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index 1fbfec995..b59effd03 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -140,10 +140,12 @@ int main() { worldserver = new WorldServer; - // now that we can send packets to world, see if there's a - // broadcast opcode that tells the client to relog into ucs + worldserver->ActivateBroadcastServerReadyTimer(); while(RunLoops) { + // this triggers clients to 'reconnect' to ucs after server crash + if (worldserver->HasBroadcastServerReadyTimer()) + worldserver->ProcessBroadcastServerReady(); Timer::SetCurrentTime(); diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index b5251b64c..556be152f 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -45,6 +45,8 @@ WorldServer::WorldServer() { m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "UCS", Config->SharedKey)); m_connection->OnMessage(std::bind(&WorldServer::ProcessMessage, this, std::placeholders::_1, std::placeholders::_2)); + + m_bsr_timer = nullptr; } WorldServer::~WorldServer() @@ -134,3 +136,37 @@ void WorldServer::ProcessClientVersionRequests(std::list& id_list) { } id_list.clear(); } + +void WorldServer::ProcessBroadcastServerReady() { + if (m_bsr_timer && (*m_bsr_timer) <= Timer::GetCurrentTime()) { + UCSBroadcastServerReady_Struct bsr; + memset(&bsr, 0, sizeof(UCSBroadcastServerReady_Struct)); + + sprintf(bsr.chat_prefix, "%s,%i,%s.", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str() + ); + sprintf(bsr.mail_prefix, "%s,%i,%s.", + Config->ChatHost.c_str(), + Config->MailPort, + Config->ShortName.c_str() + ); + + EQ::Net::DynamicPacket dp_bsr; + dp_bsr.PutData(0, &bsr, sizeof(UCSBroadcastServerReady_Struct)); + m_connection->Send(ServerOP_UCSBroadcastServerReady, dp_bsr); + + safe_delete(m_bsr_timer); + } +} + +void WorldServer::ActivateBroadcastServerReadyTimer() { + safe_delete(m_bsr_timer); + m_bsr_timer = new uint32; + + // clients do not drop their connection to ucs immediately... + // it can take upwards of 60 seconds to process the drop + // and clients will not re-connect to ucs until that occurs + *m_bsr_timer = (Timer::GetCurrentTime() + (RuleI(Chat, UCSBroadcastServerReadyDelay) * 1000)); +} diff --git a/ucs/worldserver.h b/ucs/worldserver.h index 53f52823a..66ed0ec32 100644 --- a/ucs/worldserver.h +++ b/ucs/worldserver.h @@ -30,8 +30,13 @@ public: void ProcessMessage(uint16 opcode, EQ::Net::Packet &); void ProcessClientVersionRequests(std::list& id_list); + + void ProcessBroadcastServerReady(); + bool HasBroadcastServerReadyTimer() { return (m_bsr_timer != nullptr); } + void ActivateBroadcastServerReadyTimer(); private: + uint32* m_bsr_timer; std::unique_ptr m_connection; }; diff --git a/world/ucs.cpp b/world/ucs.cpp index dfa8e116a..76cba7e4b 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -52,6 +52,7 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) Log(Logs::Detail, Logs::UCS_Server, "Got authentication from UCS when they are already authenticated."); break; } + case ServerOP_UCSBroadcastServerReady: case ServerOP_UCSClientVersionRequest: { zoneserver_list.SendPacket(pack); diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 0b118ff2e..cbf901cf7 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -516,23 +516,6 @@ void WorldDatabase::GetLauncherList(std::vector &rl) { } -void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) -{ - char MailKeyString[17]; - - if(RuleB(Chat, EnableMailKeyIPVerification) == true) - sprintf(MailKeyString, "%08X%08X", IPAddress, MailKey); - else - sprintf(MailKeyString, "%08X", MailKey); - - std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", - MailKeyString, CharID); - auto results = QueryDatabase(query); - if (!results.Success()) - Log(Logs::General, Logs::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); - -} - bool WorldDatabase::GetCharacterLevel(const char *name, int &level) { std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name); diff --git a/world/worlddb.h b/world/worlddb.h index b0c2ff221..2afd69920 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -34,7 +34,6 @@ public: int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); - void SetMailKey(int CharID, int IPAddress, int MailKey); bool GetCharacterLevel(const char *name, int &level); bool LoadCharacterCreateAllocations(); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 1f771d131..4ad2267b3 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1813,6 +1813,57 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_UCSBroadcastServerReady: + { + UCSBroadcastServerReady_Struct* bsr = (UCSBroadcastServerReady_Struct*)pack->pBuffer; + EQApplicationPacket* outapp = nullptr; + std::string buffer; + + for (auto liter : entity_list.GetClientList()) { + auto c = liter.second; + if (!c) + continue; + + int MailKey = zone->random.Int(1, INT_MAX); + + database.SetMailKey(c->CharacterID(), c->GetIP(), MailKey); + + char ConnectionType; + + // chat server packet + if (c->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) + ConnectionType = 'U'; + else if (c->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater) + ConnectionType = 'S'; + else + ConnectionType = 'C'; + + buffer = bsr->chat_prefix; + buffer.append(StringFormat("%s,%c%08X", c->GetName(), ConnectionType, MailKey)); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + if (c->ClientVersionBit() & EQEmu::versions::bit_TitaniumAndEarlier) + ConnectionType = 'M'; + + buffer = bsr->mail_prefix; + buffer.append(StringFormat("%s,%c%08X", c->GetName(), ConnectionType, MailKey)); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + } + break; + } case ServerOP_UCSClientVersionRequest: { UCSClientVersionRequest_Struct* cvreq = (UCSClientVersionRequest_Struct*)pack->pBuffer; From 1e316102ea6811b5243e414e26f3de71207072d7 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 26 Feb 2018 22:38:14 -0500 Subject: [PATCH 18/59] Fix for UCS-based messages containing saylinks --- ucs/chatchannel.cpp | 144 +++++++++++++++++++++++++++++++++++++++++++- ucs/clientlist.cpp | 2 +- ucs/worldserver.cpp | 131 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 274 insertions(+), 3 deletions(-) diff --git a/ucs/chatchannel.cpp b/ucs/chatchannel.cpp index 16403c295..11a000712 100644 --- a/ucs/chatchannel.cpp +++ b/ucs/chatchannel.cpp @@ -28,6 +28,10 @@ extern Database database; extern uint32 ChatMessagesSent; +void ServerToClient45SayLink(std::string& clientSayLink, const std::string& serverSayLink); +void ServerToClient50SayLink(std::string& clientSayLink, const std::string& serverSayLink); +void ServerToClient55SayLink(std::string& clientSayLink, const std::string& serverSayLink); + ChatChannel::ChatChannel(std::string inName, std::string inOwner, std::string inPassword, bool inPermanent, int inMinimumStatus) : DeleteTimer(0) { @@ -384,6 +388,8 @@ void ChatChannel::SendMessageToChannel(std::string Message, Client* Sender) { if(!Sender) return; + std::string cv_messages[EQEmu::versions::ClientVersionCount]; + ChatMessagesSent++; LinkedListIterator iterator(ClientsInChannel); @@ -398,7 +404,28 @@ void ChatChannel::SendMessageToChannel(std::string Message, Client* Sender) { { Log(Logs::Detail, Logs::UCS_Server, "Sending message to %s from %s", ChannelClient->GetName().c_str(), Sender->GetName().c_str()); - ChannelClient->SendChannelMessage(Name, Message, Sender); + + if (cv_messages[static_cast(ChannelClient->GetClientVersion())].length() == 0) { + switch (ChannelClient->GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ServerToClient45SayLink(cv_messages[static_cast(ChannelClient->GetClientVersion())], Message); + break; + case EQEmu::versions::ClientVersion::SoF: + case EQEmu::versions::ClientVersion::SoD: + case EQEmu::versions::ClientVersion::UF: + ServerToClient50SayLink(cv_messages[static_cast(ChannelClient->GetClientVersion())], Message); + break; + case EQEmu::versions::ClientVersion::RoF: + ServerToClient55SayLink(cv_messages[static_cast(ChannelClient->GetClientVersion())], Message); + break; + case EQEmu::versions::ClientVersion::RoF2: + default: + cv_messages[static_cast(ChannelClient->GetClientVersion())] = Message; + break; + } + } + + ChannelClient->SendChannelMessage(Name, cv_messages[static_cast(ChannelClient->GetClientVersion())], Sender); } iterator.Advance(); @@ -655,3 +682,118 @@ std::string CapitaliseName(std::string inString) { return NormalisedName; } +void ServerToClient45SayLink(std::string& clientSayLink, const std::string& serverSayLink) { + if (serverSayLink.find('\x12') == std::string::npos) { + clientSayLink = serverSayLink; + return; + } + + auto segments = SplitString(serverSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 56) { + clientSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) + // Diff: ^^^^^ ^ ^^^^^ + + clientSayLink.push_back('\x12'); + clientSayLink.append(segments[segment_iter].substr(0, 31)); + clientSayLink.append(segments[segment_iter].substr(36, 5)); + + if (segments[segment_iter][41] == '0') + clientSayLink.push_back(segments[segment_iter][42]); + else + clientSayLink.push_back('F'); + + clientSayLink.append(segments[segment_iter].substr(48)); + clientSayLink.push_back('\x12'); + } + else { + clientSayLink.append(segments[segment_iter]); + } + } +} + +void ServerToClient50SayLink(std::string& clientSayLink, const std::string& serverSayLink) { + if (serverSayLink.find('\x12') == std::string::npos) { + clientSayLink = serverSayLink; + return; + } + + auto segments = SplitString(serverSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 56) { + clientSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // Diff: ^^^^^ ^ + + clientSayLink.push_back('\x12'); + clientSayLink.append(segments[segment_iter].substr(0, 31)); + clientSayLink.append(segments[segment_iter].substr(36, 5)); + + if (segments[segment_iter][41] == '0') + clientSayLink.push_back(segments[segment_iter][42]); + else + clientSayLink.push_back('F'); + + clientSayLink.append(segments[segment_iter].substr(43)); + clientSayLink.push_back('\x12'); + } + else { + clientSayLink.append(segments[segment_iter]); + } + } +} + +void ServerToClient55SayLink(std::string& clientSayLink, const std::string& serverSayLink) { + if (serverSayLink.find('\x12') == std::string::npos) { + clientSayLink = serverSayLink; + return; + } + + auto segments = SplitString(serverSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 56) { + clientSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55) + // Diff: ^ + + clientSayLink.push_back('\x12'); + clientSayLink.append(segments[segment_iter].substr(0, 41)); + + if (segments[segment_iter][41] == '0') + clientSayLink.push_back(segments[segment_iter][42]); + else + clientSayLink.push_back('F'); + + clientSayLink.append(segments[segment_iter].substr(43)); + clientSayLink.push_back('\x12'); + } + else { + clientSayLink.append(segments[segment_iter]); + } + } +} diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index 751cf4089..e5076a150 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -893,7 +893,7 @@ void Clientlist::RequestClientVersion(uint32 character_id) { bool Clientlist::QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version) { if (!character_id) return true; - if (client_version == EQEmu::versions::ClientVersion::Unknown) + if (client_version < EQEmu::versions::ClientVersion::Titanium || client_version > EQEmu::versions::ClientVersion::RoF2) return false; if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index 556be152f..48ec03dca 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/misc_functions.h" #include "../common/packet_functions.h" #include "../common/md5.h" +#include "../common/string_util.h" #include "worldserver.h" #include "clientlist.h" #include "ucsconfig.h" @@ -41,6 +42,10 @@ extern Database database; void ProcessMailTo(Client *c, std::string from, std::string subject, std::string message); +void Client45ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink); +void Client50ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink); +void Client55ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink); + WorldServer::WorldServer() { m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "UCS", Config->SharedKey)); @@ -96,7 +101,26 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) if (Message[0] == ';') { - c->SendChannelMessageByNumber(Message.substr(1, std::string::npos)); + std::string new_message; + switch (c->GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + Client45ToServerSayLink(new_message, Message.substr(1, std::string::npos)); + break; + case EQEmu::versions::ClientVersion::SoF: + case EQEmu::versions::ClientVersion::SoD: + case EQEmu::versions::ClientVersion::UF: + Client50ToServerSayLink(new_message, Message.substr(1, std::string::npos)); + break; + case EQEmu::versions::ClientVersion::RoF: + Client55ToServerSayLink(new_message, Message.substr(1, std::string::npos)); + break; + case EQEmu::versions::ClientVersion::RoF2: + default: + new_message = Message.substr(1, std::string::npos); + break; + } + + c->SendChannelMessageByNumber(new_message); } else if (Message[0] == '[') { @@ -170,3 +194,108 @@ void WorldServer::ActivateBroadcastServerReadyTimer() { // and clients will not re-connect to ucs until that occurs *m_bsr_timer = (Timer::GetCurrentTime() + (RuleI(Chat, UCSBroadcastServerReadyDelay) * 1000)); } + +void Client45ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { + if (clientSayLink.find('\x12') == std::string::npos) { + serverSayLink = clientSayLink; + return; + } + + auto segments = SplitString(clientSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 45) { + serverSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // Idx: 0 1 6 11 16 21 26 31 32 36 37 (Source) + // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^^^^^ ^ ^^^^^ + + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.push_back(segments[segment_iter][36]); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(37)); + serverSayLink.push_back('\x12'); + } + else { + serverSayLink.append(segments[segment_iter]); + } + } +} + +void Client50ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { + if (clientSayLink.find('\x12') == std::string::npos) { + serverSayLink = clientSayLink; + return; + } + + auto segments = SplitString(clientSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 50) { + serverSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // Idx: 0 1 6 11 16 21 26 31 32 36 37 42 (Source) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^^^^^ ^ + + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); + } + else { + serverSayLink.append(segments[segment_iter]); + } + } +} + +void Client55ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { + if (clientSayLink.find('\x12') == std::string::npos) { + serverSayLink = clientSayLink; + return; + } + + auto segments = SplitString(clientSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 55) { + serverSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 42 47 (Source) + // RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^ + + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 41)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(41)); + serverSayLink.push_back('\x12'); + } + else { + serverSayLink.append(segments[segment_iter]); + } + } +} From 6f49a379149d046755e0aae50f69c62dc79f6c58 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 26 Feb 2018 23:04:28 -0500 Subject: [PATCH 19/59] Fix for linux build fail --- zone/worldserver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 4ad2267b3..09e5aa223 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include #ifdef _WINDOWS #include From 29f89cdfbc6f4a67c49a407a507766b2df3dd12c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 27 Feb 2018 23:42:18 -0500 Subject: [PATCH 20/59] NPCs parsed less than normal push Checking the packets, I was getting 0.3 for bash against PCs and 0.03 against NPCs. Same for other attacks. --- zone/attack.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/attack.cpp b/zone/attack.cpp index 58e1510ff..8fcf508c3 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3615,6 +3615,8 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() && (IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) { a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used); + if (IsNPC()) + a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC // update NPC stuff auto new_pos = glm::vec3(m_Position.x + (a->force * std::cos(a->meleepush_xy) + m_Delta.x), m_Position.y + (a->force * std::sin(a->meleepush_xy) + m_Delta.y), m_Position.z); From 06d22fa009b313a51511c2c54a99006627fb91c4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 28 Feb 2018 19:21:03 -0500 Subject: [PATCH 21/59] Identify the rest of Action/CombatDamage structs --- common/eq_packet_structs.h | 33 ++++++++-------- common/patches/rof.cpp | 31 ++++++++------- common/patches/rof2.cpp | 31 ++++++++------- common/patches/rof2_structs.h | 52 +++++++++++------------- common/patches/rof_structs.h | 52 +++++++++++------------- common/patches/sod.cpp | 15 ++++--- common/patches/sod_structs.h | 65 ++++++++++++++---------------- common/patches/sof.cpp | 15 ++++--- common/patches/sof_structs.h | 66 +++++++++++++++---------------- common/patches/titanium.cpp | 11 ++++-- common/patches/titanium_structs.h | 28 ++++++------- common/patches/uf.cpp | 39 +++++++----------- common/patches/uf_structs.h | 59 +++++++++++++-------------- zone/attack.cpp | 6 +-- zone/spell_effects.cpp | 18 ++++----- zone/spells.cpp | 28 ++++++------- zone/trap.cpp | 2 +- 17 files changed, 261 insertions(+), 290 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index cc06ecf15..fd09f4d56 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1253,21 +1253,22 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; - /* 08 */ uint32 bard_focus_id; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ uint32 sequence; - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 unknown29; + /* 29 */ uint8 spell_level; // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made - /* 31 */ + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made +// newer clients have some data for setting LaunchSpellData when effect_flag & 4 +// /* 31 */ uint8 spell_gem; +// /* 32 */ uint32 inventory_slot; +// /* 36 */ uint32 item_cast_type; }; // this is what prints the You have been struck. and the regular @@ -1277,12 +1278,12 @@ struct CombatDamage_Struct { /* 00 */ uint16 target; /* 02 */ uint16 source; -/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill /* 05 */ uint16 spellid; /* 07 */ uint32 damage; /* 11 */ float force; -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; /* 23 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 24fecb2ca..62ef6751a 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -163,22 +163,23 @@ namespace RoF OUT(level); eq->unknown06 = 0; eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->bard_focus_id = emu->bard_focus_id; - eq->knockback_angle = emu->sequence; - eq->unknown22 = 0; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); eq->damage = 0; eq->unknown31 = 0; OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown39 = 14; - eq->unknown43 = 0; - eq->unknown44 = 17; - eq->unknown45 = 0; - eq->unknown46 = -1; - eq->unknown50 = 0; - eq->unknown54 = 0; + OUT(spell_level); + OUT(effect_flag); + eq->spell_gem = 0; + eq->slot.Type = INVALID_INDEX; + eq->slot.Unknown02 = 0; + eq->slot.Slot = INVALID_INDEX; + eq->slot.SubIndex = INVALID_INDEX; + eq->slot.AugIndex = INVALID_INDEX; + eq->slot.Unknown01 = 0; + eq->item_cast_type = 0; FINISH_ENCODE(); } @@ -659,8 +660,8 @@ namespace RoF OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -4466,7 +4467,7 @@ namespace RoF IN(type); IN(spellid); IN(damage); - IN(meleepush_xy); + IN(hit_heading); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 7e5d79bb6..5a30c1695 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -232,22 +232,23 @@ namespace RoF2 OUT(level); eq->unknown06 = 0; eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->bard_focus_id = emu->bard_focus_id; - eq->knockback_angle = emu->sequence; - eq->unknown22 = 0; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); eq->damage = 0; eq->unknown31 = 0; OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown39 = 14; - eq->unknown43 = 0; - eq->unknown44 = 17; - eq->unknown45 = 0; - eq->unknown46 = -1; - eq->unknown50 = 0; - eq->unknown54 = 0; + OUT(spell_level); + OUT(effect_flag); + eq->spell_gem = 0; + eq->slot.Type = INVALID_INDEX; + eq->slot.Unknown02 = 0; + eq->slot.Slot = INVALID_INDEX; + eq->slot.SubIndex = INVALID_INDEX; + eq->slot.AugIndex = INVALID_INDEX; + eq->slot.Unknown01 = 0; + eq->item_cast_type = 0; FINISH_ENCODE(); } @@ -727,8 +728,8 @@ namespace RoF2 OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -4706,7 +4707,7 @@ namespace RoF2 IN(type); IN(spellid); IN(damage); - IN(meleepush_xy); + IN(hit_heading); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index bd18a13c0..5be96e071 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -1462,17 +1462,17 @@ struct Action_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? /*39*/ }; @@ -1484,25 +1484,21 @@ struct ActionAlt_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? -/*39*/ uint32 unknown39; // New field to Underfoot - Seen 14 -/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 -/*44*/ uint8 unknown44; // New field to Underfoot - Seen 17 -/*45*/ uint8 unknown45; // New field to Underfoot - Seen 0 -/*46*/ int32 unknown46; // New field to Underfoot - Seen -1 -/*50*/ uint32 unknown50; // New field to Underfoot - Seen 0 -/*54*/ uint16 unknown54; // New field to Underfoot - Seen 0 +/*39*/ uint8 spell_gem; +/*40*/ InventorySlot_Struct slot; +/*52*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*56*/ }; @@ -1517,9 +1513,9 @@ struct CombatDamage_Struct /* 05 */ uint32 spellid; /* 09 */ int32 damage; /* 13 */ float force; // cd cc cc 3d -/* 17 */ float meleepush_xy; // see above notes in Action_Struct -/* 21 */ float meleepush_z; -/* 25 */ uint8 unknown25; // was [9] +/* 17 */ float hit_heading; // see above notes in Action_Struct +/* 21 */ float hit_pitch; +/* 25 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 26 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 30 */ }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index d38d5c6bb..08e3647be 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -1450,17 +1450,17 @@ struct Action_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? /*39*/ }; @@ -1472,25 +1472,21 @@ struct ActionAlt_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? -/*39*/ uint32 unknown39; // New field to Underfoot - Seen 14 -/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 -/*44*/ uint8 unknown44; // New field to Underfoot - Seen 17 -/*45*/ uint8 unknown45; // New field to Underfoot - Seen 0 -/*46*/ int32 unknown46; // New field to Underfoot - Seen -1 -/*50*/ uint32 unknown50; // New field to Underfoot - Seen 0 -/*54*/ uint16 unknown54; // New field to Underfoot - Seen 0 +/*39*/ uint8 spell_gem; +/*40*/ InventorySlot_Struct slot; +/*52*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*56*/ }; @@ -1505,9 +1501,9 @@ struct CombatDamage_Struct /* 05 */ uint32 spellid; /* 09 */ int32 damage; /* 13 */ float force; // cd cc cc 3d -/* 17 */ float meleepush_xy; // see above notes in Action_Struct -/* 21 */ float meleepush_z; -/* 25 */ uint8 unknown25; // was [9] +/* 17 */ float hit_heading; // see above notes in Action_Struct +/* 21 */ float hit_pitch; +/* 25 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 26 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 30 */ }; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 9e1479188..560151bfa 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -161,15 +161,14 @@ namespace SoD OUT(source); OUT(level); OUT(instrument_mod); - eq->sequence = emu->sequence; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); //OUT(damage); OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; + OUT(spell_level); + OUT(effect_flag); // if this is 4, a buff icon is made FINISH_ENCODE(); } @@ -458,8 +457,8 @@ namespace SoD OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 866418b54..6b5c2f72c 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -1215,20 +1215,18 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; // seems to be fixed to 0x0A - /* 08 */ uint32 unknown08; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ float sequence; // was uint32 - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 level2; // level of caster again? Or maybe the castee + /* 29 */ uint8 spell_level; // level of caster again? Or maybe the castee // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made /* 31 */ }; @@ -1237,26 +1235,23 @@ struct Action_Struct // has to do with buff blocking?? struct ActionAlt_Struct // ActionAlt_Struct - Size: 56 bytes { -/*0000*/ uint16 target; // Target ID -/*0002*/ uint16 source; // SourceID -/*0004*/ uint16 level; // level of caster -/*0006*/ uint16 instrument_mod; // seems to be fixed to 0x0A -/*0008*/ uint32 unknown08; -/*0012*/ uint16 unknown16; -/*0014*/ uint32 sequence; -/*0018*/ uint32 unknown18; -/*0022*/ uint8 type; // Casts, Falls, Bashes, etc... -/*0023*/ uint32 damage; // Amount of Damage -/*0027*/ uint16 spell; // SpellID -/*0029*/ uint8 unknown29; -/*0030*/ uint8 buff_unknown; // if this is 4, a buff icon is made -/*0031*/ uint32 unknown0031; // seen 00 00 00 00 -/*0035*/ uint8 unknown0035; // seen 00 -/*0036*/ uint32 unknown0036; // seen ff ff ff ff -/*0040*/ uint32 unknown0040; // seen ff ff ff ff -/*0044*/ uint32 unknown0044; // seen ff ff ff ff -/*0048*/ uint32 unknown0048; // seen 00 00 00 00 -/*0052*/ uint32 unknown0052; // seen 00 00 00 00 +/*0000*/ uint16 target; // id of target +/*0002*/ uint16 source; // id of caster +/*0004*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*0006*/ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) +/*0010*/ float force; +/*0014*/ float hit_heading; +/*0018*/ float hit_pitch; +/*0022*/ uint8 type; // 231 (0xE7) for spells, skill +/*0023*/ uint16 unknown23; // OSX says min_damage +/*0025*/ uint16 unknown25; // OSX says tohit +/*0027*/ uint16 spell; // spell id being cast +/*0029*/ uint8 spell_level; // level of caster again? Or maybe the castee +// this field seems to be some sort of success flag, if it's 4 +/*0030*/ uint8 effect_flag; // if this is 4, a buff icon is made +/*0031*/ uint8 spell_slot; +/*0032*/ uint32 slot[5]; +/*0052*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*0056*/ }; @@ -1271,9 +1266,9 @@ struct CombatDamage_Struct /* 05 */ uint16 spellid; /* 07 */ int32 damage; /* 11 */ float force; // cd cc cc 3d -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; -/* 23 */ uint8 unknown23; // was [9] +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; +/* 23 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 24 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 28 */ }; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 063d99ca4..c16bed086 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -161,15 +161,14 @@ namespace SoF OUT(source); OUT(level); OUT(instrument_mod); - eq->sequence = emu->sequence; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); //OUT(damage); OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; + OUT(spell_level); + OUT(effect_flag); // if this is 4, a buff icon is made FINISH_ENCODE(); } @@ -440,8 +439,8 @@ namespace SoF OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); FINISH_ENCODE(); } diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 168dfb123..29272a7a6 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -1215,20 +1215,18 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; // seems to be fixed to 0x0A - /* 08 */ uint32 unknown08; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ float sequence; // was uint32 - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 level2; // level of caster again? Or maybe the castee + /* 29 */ uint8 spell_level; // level of caster again? Or maybe the castee // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made /* 31 */ }; @@ -1237,26 +1235,23 @@ struct Action_Struct // has to do with buff blocking?? struct ActionAlt_Struct // ActionAlt_Struct - Size: 56 bytes { -/*0000*/ uint16 target; // Target ID -/*0002*/ uint16 source; // SourceID -/*0004*/ uint16 level; // level of caster -/*0006*/ uint16 instrument_mod; // seems to be fixed to 0x0A -/*0008*/ uint32 unknown08; -/*0012*/ uint16 unknown16; -/*0014*/ uint32 sequence; -/*0018*/ uint32 unknown18; -/*0022*/ uint8 type; // Casts, Falls, Bashes, etc... -/*0023*/ uint32 damage; // Amount of Damage -/*0027*/ uint16 spell; // SpellID -/*0029*/ uint8 unknown29; -/*0030*/ uint8 buff_unknown; // if this is 4, a buff icon is made -/*0031*/ uint32 unknown0031; // seen 00 00 00 00 -/*0035*/ uint8 unknown0035; // seen 00 -/*0036*/ uint32 unknown0036; // seen ff ff ff ff -/*0040*/ uint32 unknown0040; // seen ff ff ff ff -/*0044*/ uint32 unknown0044; // seen ff ff ff ff -/*0048*/ uint32 unknown0048; // seen 00 00 00 00 -/*0052*/ uint32 unknown0052; // seen 00 00 00 00 +/*0000*/ uint16 target; // id of target +/*0002*/ uint16 source; // id of caster +/*0004*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*0006*/ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) +/*0010*/ float force; +/*0014*/ float hit_heading; +/*0018*/ float hit_pitch; +/*0022*/ uint8 type; // 231 (0xE7) for spells, skill +/*0023*/ uint16 unknown23; // OSX says min_damage +/*0025*/ uint16 unknown25; // OSX says tohit +/*0027*/ uint16 spell; // spell id being cast +/*0029*/ uint8 spell_level; // level of caster again? Or maybe the castee +// this field seems to be some sort of success flag, if it's 4 +/*0030*/ uint8 effect_flag; // if this is 4, a buff icon is made +/*0031*/ uint8 spell_slot; +/*0032*/ uint32 slot[5]; +/*0052*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*0056*/ }; @@ -1271,9 +1266,10 @@ struct CombatDamage_Struct /* 05 */ uint16 spellid; /* 07 */ int32 damage; /* 11 */ float force; // cd cc cc 3d -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; -/* 23 */ uint8 unknown23[5]; // was [9] this appears unrelated to the stuff the other clients do here? +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; +/* 23 */ uint8 secondary; // 0 for primary hand, 1 for secondary +/* 24 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage, Report function doesn't seem to check this :P /* 28 */ }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index afff0cea2..bf3c3d359 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -164,11 +164,14 @@ namespace Titanium OUT(source); OUT(level); OUT(instrument_mod); - OUT(sequence); + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); //OUT(damage); OUT(spell); - OUT(buff_unknown); // if this is 4, a buff icon is made + OUT(spell_level); + OUT(effect_flag); // if this is 4, a buff icon is made FINISH_ENCODE(); } @@ -358,8 +361,8 @@ namespace Titanium OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); FINISH_ENCODE(); } diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index ea698acf0..9f2d9423d 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -1119,20 +1119,18 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; - /* 08 */ uint32 unknown08; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ uint32 sequence; - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 unknown29; + /* 29 */ uint8 spell_level; // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made /* 31 */ }; @@ -1143,12 +1141,12 @@ struct CombatDamage_Struct { /* 00 */ uint16 target; /* 02 */ uint16 source; -/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill /* 05 */ uint16 spellid; /* 07 */ uint32 damage; /* 11 */ float force; -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; /* 23 */ }; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 176e737b7..79fdca333 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -161,29 +161,20 @@ namespace UF OUT(source); OUT(level); eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->knockback_angle = emu->sequence; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown37 = 0x01; - eq->unknown44 = 0xFFFFFFFF; - eq->unknown48 = 0xFFFFFFFF; - eq->unknown52 = 0xFFFFFFFF; - - /*OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1;*/ + OUT(spell_level); + OUT(effect_flag); + eq->spell_gem = 0; + eq->slot[0] = -1; // type + eq->slot[1] = -1; // slot + eq->slot[2] = -1; // sub index + eq->slot[3] = -1; // aug index + eq->slot[4] = -1; // unknown + eq->item_cast_type = 0; FINISH_ENCODE(); } @@ -586,8 +577,8 @@ namespace UF OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -3398,7 +3389,7 @@ namespace UF IN(type); IN(spellid); IN(damage); - IN(meleepush_xy); + IN(hit_heading); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 8a03144c9..dc66afd43 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -1252,19 +1252,19 @@ struct Action_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; -/*10*/ uint16 instrument_focus; -/*12*/ uint16 unknown12; // seems to always be set to something and it doesn't change between casts except in special cases like changing instrument mods -/*14*/ uint32 unknown14; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( +/*10*/ float instrument_mod; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint16 spell; // spell id being cast -/*35*/ uint8 level2; // level of caster again? Or maybe the castee +/*35*/ uint8 spell_level; // level of caster again? Or maybe the castee /*36*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? +/*37*/ }; @@ -1276,27 +1276,22 @@ struct ActionAlt_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 unknown14; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint16 spell; // spell id being cast -/*35*/ uint8 level2; // level of caster again? Or maybe the castee +/*35*/ uint8 spell_level; // level of caster again? Or maybe the castee /*36*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? -/*37*/ uint32 unknown37; // New field to Underfoot - Seen 14 -/*41*/ uint8 unknown41; // New field to Underfoot - Seen 0 -/*42*/ uint8 unknown42; // New field to Underfoot - Seen 0 -/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 -/*44*/ uint32 unknown44; // New field to Underfoot - Seen 23 -/*48*/ uint32 unknown48; // New field to Underfoot - Seen -1 -/*52*/ uint32 unknown52; // New field to Underfoot - Seen -1 -/*56*/ uint32 unknown56; // New field to Underfoot - Seen 0 -/*60*/ uint32 unknown60; // New field to Underfoot - Seen 0 +/*37*/ uint8 spell_gem; +/*38*/ uint8 padding38[2]; +/*40*/ uint32 slot[5]; +/*60*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*64*/ }; @@ -1311,9 +1306,9 @@ struct CombatDamage_Struct /* 05 */ uint16 spellid; /* 07 */ int32 damage; /* 11 */ float force; // cd cc cc 3d -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; -/* 23 */ uint8 unknown23; // was [9] +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; +/* 23 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 24 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 28 */ }; diff --git a/zone/attack.cpp b/zone/attack.cpp index 8fcf508c3..0979a705e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3611,15 +3611,15 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const a->special = 2; else a->special = 0; - a->meleepush_xy = attacker ? attacker->GetHeading() : 0.0f; + a->hit_heading = attacker ? attacker->GetHeading() : 0.0f; if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() && (IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) { a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used); if (IsNPC()) a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC // update NPC stuff - auto new_pos = glm::vec3(m_Position.x + (a->force * std::cos(a->meleepush_xy) + m_Delta.x), - m_Position.y + (a->force * std::sin(a->meleepush_xy) + m_Delta.y), m_Position.z); + auto new_pos = glm::vec3(m_Position.x + (a->force * std::cos(a->hit_heading) + m_Delta.x), + m_Position.y + (a->force * std::sin(a->hit_heading) + m_Delta.y), m_Position.z); if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. if (IsNPC()) { // Is this adequate? diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 480479b2f..f55c091d6 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -913,16 +913,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = static_cast((GetHeading() * 12345 / 2)); + action->hit_heading = GetHeading(); action->type = 231; action->spell = spell_id; - action->buff_unknown = 4; + action->effect_flag = 4; cd->target = action->target; cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; CastToClient()->QueuePacket(action_packet); if(caster && caster->IsClient() && caster != this) @@ -964,16 +964,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = static_cast((GetHeading() * 12345 / 2)); + action->hit_heading = GetHeading(); action->type = 231; action->spell = spell_id; - action->buff_unknown = 4; + action->effect_flag = 4; cd->target = action->target; cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) @@ -1002,16 +1002,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = static_cast((GetHeading() * 12345 / 2)); + action->hit_heading = GetHeading(); action->type = 231; action->spell = spell_id; - action->buff_unknown = 4; + action->effect_flag = 4; cd->target = action->target; cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) diff --git a/zone/spells.cpp b/zone/spells.cpp index e66a41fa8..002f996b6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2652,18 +2652,18 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->source = caster->GetID(); action->target = GetID(); action->spell = spell_id; - action->sequence = (uint32) (GetHeading()); // just some random number + action->hit_heading = GetHeading(); action->instrument_mod = caster->GetInstrumentMod(spell_id); - action->buff_unknown = 0; - action->level = buffs[buffs_i].casterlevel; + action->effect_flag = 0; + action->spell_level = action->level = buffs[buffs_i].casterlevel; action->type = DamageTypeSpell; entity_list.QueueCloseClients(this, packet, false, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); - action->buff_unknown = 4; + action->effect_flag = 4; if(IsEffectInSpell(spell_id, SE_TossUp)) { - action->buff_unknown = 0; + action->effect_flag = 0; } else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) { @@ -2673,7 +2673,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { { CastToClient()->SetKnockBackExemption(true); - action->buff_unknown = 0; + action->effect_flag = 0; auto outapp_push = new EQApplicationPacket( OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; @@ -2725,7 +2725,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { cd->source = action->source; cd->type = DamageTypeSpell; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { @@ -3536,9 +3536,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r action->level = caster_level; // caster level, for animation only action->type = 231; // 231 means a spell action->spell = spell_id; - action->sequence = (uint32) (GetHeading()); // just some random number + action->hit_heading = GetHeading(); action->instrument_mod = GetInstrumentMod(spell_id); - action->buff_unknown = 0; + action->effect_flag = 0; if(spelltar != this && spelltar->IsClient()) // send to target spelltar->CastToClient()->QueuePacket(action_packet); @@ -3965,11 +3965,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // NOTE: this is what causes the buff icon to appear on the client, if // this is a buff - but it sortof relies on the first packet. // the complete sequence is 2 actions and 1 damage message - action->buff_unknown = 0x04; // this is a success flag + action->effect_flag = 0x04; // this is a success flag if(IsEffectInSpell(spell_id, SE_TossUp)) { - action->buff_unknown = 0; + action->effect_flag = 0; } else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) { @@ -3979,7 +3979,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r { spelltar->CastToClient()->SetKnockBackExemption(true); - action->buff_unknown = 0; + action->effect_flag = 0; auto outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; @@ -4039,7 +4039,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)){ entity_list.QueueCloseClients( @@ -5722,7 +5722,7 @@ void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) a->source = this->GetID(); a->type = 231; a->spell = spell_id; - a->sequence = 231; + a->hit_heading = GetHeading(); app.priority = 1; entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); diff --git a/zone/trap.cpp b/zone/trap.cpp index 91e11c012..687d3ded3 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -216,7 +216,7 @@ void Trap::Trigger(Mob* trigger) int dmg = zone->random.Int(effectvalue, effectvalue2); trigger->SetHP(trigger->GetHP() - dmg); a->damage = dmg; - a->meleepush_xy = zone->random.Int(0, 1234567); + a->hit_heading = 0.0f; a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID(); a->spellid = 0; a->target = trigger->GetID(); From 1b849059419cae9dcb22e538bfad189cb05b849e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 28 Feb 2018 19:47:23 -0500 Subject: [PATCH 22/59] Fix spell push This now uses the spell data correctly, still need to write a system for NPCs though SE_TossUp needs to be implemented to, it just damages NPCs only --- zone/spell_effects.cpp | 55 --------------------------- zone/spells.cpp | 85 +++++------------------------------------- 2 files changed, 10 insertions(+), 130 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f55c091d6..30cf10450 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2075,61 +2075,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Toss Up: %d", effect_value); #endif - double toss_amt = (double)spells[spell_id].base[i]; - if(toss_amt < 0) - toss_amt = -toss_amt; - - if(IsNPC()) - { - Stun(static_cast(toss_amt)); - } - toss_amt = sqrt(toss_amt)-2.0; - - if(toss_amt < 0.0) - toss_amt = 0.0; - - if(toss_amt > 20.0) - toss_amt = 20.0; - - if(IsClient()) - { - CastToClient()->SetKnockBackExemption(true); - } - - double look_heading = GetHeading(); - look_heading /= 256; - look_heading *= 360; - look_heading += 180; - if(look_heading > 360) - look_heading -= 360; - - //x and y are crossed mkay - double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); - - auto outapp_push = - new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; - - spu->spawn_id = GetID(); - spu->x_pos = FloatToEQ19(GetX()); - spu->y_pos = FloatToEQ19(GetY()); - spu->z_pos = FloatToEQ19(GetZ()); - spu->delta_x = FloatToEQ13(new_x); - spu->delta_y = FloatToEQ13(new_y); - spu->delta_z = FloatToEQ13(toss_amt); - spu->heading = FloatToEQ12(GetHeading()); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; - spu->animation = 0; - spu->delta_heading = FloatToEQ10(0); - outapp_push->priority = 5; - entity_list.QueueClients(this, outapp_push, true); - if(IsClient()) - CastToClient()->FastQueuePacket(&outapp_push); - break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 002f996b6..6815ff406 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2652,7 +2652,9 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->source = caster->GetID(); action->target = GetID(); action->spell = spell_id; + action->force = spells[spell_id].pushback; action->hit_heading = GetHeading(); + action->hit_pitch = spells[spell_id].pushup; action->instrument_mod = caster->GetInstrumentMod(spell_id); action->effect_flag = 0; action->spell_level = action->level = buffs[buffs_i].casterlevel; @@ -2661,11 +2663,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->effect_flag = 4; - if(IsEffectInSpell(spell_id, SE_TossUp)) - { - action->effect_flag = 0; - } - else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) { if(IsClient()) { @@ -2673,38 +2671,6 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { { CastToClient()->SetKnockBackExemption(true); - action->effect_flag = 0; - auto outapp_push = new EQApplicationPacket( - OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; - - double look_heading = caster->CalculateHeadingToTarget(GetX(), GetY()); - look_heading /= 256; - look_heading *= 360; - if(look_heading > 360) - look_heading -= 360; - - //x and y are crossed mkay - double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); - - spu->spawn_id = GetID(); - spu->x_pos = FloatToEQ19(GetX()); - spu->y_pos = FloatToEQ19(GetY()); - spu->z_pos = FloatToEQ19(GetZ()); - spu->delta_x = FloatToEQ13(new_x); - spu->delta_y = FloatToEQ13(new_y); - spu->delta_z = FloatToEQ13(spells[spell_id].pushup); - spu->heading = FloatToEQ12(GetHeading()); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; - spu->animation = 0; - spu->delta_heading = FloatToEQ10(0); - outapp_push->priority = 6; - entity_list.QueueClients(this, outapp_push, true); - CastToClient()->FastQueuePacket(&outapp_push); } } } @@ -2725,7 +2691,9 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { cd->source = action->source; cd->type = DamageTypeSpell; cd->spellid = action->spell; + cd->force = action->force; cd->hit_heading = action->hit_heading; + cd->hit_pitch = action->hit_pitch; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { @@ -3536,7 +3504,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r action->level = caster_level; // caster level, for animation only action->type = 231; // 231 means a spell action->spell = spell_id; + action->force = spells[spell_id].pushback; action->hit_heading = GetHeading(); + action->hit_pitch = spells[spell_id].pushup; action->instrument_mod = GetInstrumentMod(spell_id); action->effect_flag = 0; @@ -3967,50 +3937,13 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // the complete sequence is 2 actions and 1 damage message action->effect_flag = 0x04; // this is a success flag - if(IsEffectInSpell(spell_id, SE_TossUp)) - { - action->effect_flag = 0; - } - else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) { if(spelltar->IsClient()) { if(!IsBuffSpell(spell_id)) { spelltar->CastToClient()->SetKnockBackExemption(true); - - action->effect_flag = 0; - auto outapp_push = - new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; - - double look_heading = CalculateHeadingToTarget(spelltar->GetX(), spelltar->GetY()); - look_heading /= 256; - look_heading *= 360; - if(look_heading > 360) - look_heading -= 360; - - //x and y are crossed mkay - double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); - - spu->spawn_id = spelltar->GetID(); - spu->x_pos = FloatToEQ19(spelltar->GetX()); - spu->y_pos = FloatToEQ19(spelltar->GetY()); - spu->z_pos = FloatToEQ19(spelltar->GetZ()); - spu->delta_x = FloatToEQ13(new_x); - spu->delta_y = FloatToEQ13(new_y); - spu->delta_z = FloatToEQ13(spells[spell_id].pushup); - spu->heading = FloatToEQ12(spelltar->GetHeading()); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; - spu->animation = 0; - spu->delta_heading = FloatToEQ10(0); - outapp_push->priority = 6; - entity_list.QueueClients(this, outapp_push, true); - spelltar->CastToClient()->FastQueuePacket(&outapp_push); } } } @@ -4039,7 +3972,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; + cd->force = action->force; cd->hit_heading = action->hit_heading; + cd->hit_pitch = action->hit_pitch; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)){ entity_list.QueueCloseClients( From 8e4aff391e97c218dd2519f6f581c86e953fbd0d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 11:29:13 -0500 Subject: [PATCH 23/59] Fix buff issue --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 6815ff406..0e643077e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3501,7 +3501,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r action->target = spelltar->GetID(); } - action->level = caster_level; // caster level, for animation only + action->spell_level = action->level = caster_level; // caster level, for animation only action->type = 231; // 231 means a spell action->spell = spell_id; action->force = spells[spell_id].pushback; From 7edc89160543ca30a5efd2f2a53f28fb4c73555e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 13:41:02 -0500 Subject: [PATCH 24/59] Use the melee push pos update timer for rate limiting the pushes Also lowered the timer since it's limiting actual movement now --- zone/attack.cpp | 6 ++---- zone/mob.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 0979a705e..7648f566a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3620,14 +3620,12 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const // update NPC stuff auto new_pos = glm::vec3(m_Position.x + (a->force * std::cos(a->hit_heading) + m_Delta.x), m_Position.y + (a->force * std::sin(a->hit_heading) + m_Delta.y), m_Position.z); - if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. + if (position_update_melee_push_timer.Check() && zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. if (IsNPC()) { // Is this adequate? Teleport(new_pos); - if (position_update_melee_push_timer.Check()) { - SendPositionUpdate(); - } + SendPositionUpdate(); } } else { diff --git a/zone/mob.cpp b/zone/mob.cpp index 3e9aff746..6c3808549 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -115,7 +115,7 @@ Mob::Mob(const char* in_name, fix_z_timer(300), fix_z_timer_engaged(100), attack_anim_timer(1000), - position_update_melee_push_timer(1000), + position_update_melee_push_timer(500), mHateListCleanup(6000) { targeted = 0; From d71e79b3065d282dc3f803893d1340240599e080 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 13:44:10 -0500 Subject: [PATCH 25/59] Add 2013 NPC vs NPC push change They remove NPC vs NPC melee push in 2013 --- zone/attack.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 7648f566a..457518c67 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3615,21 +3615,30 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() && (IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) { a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used); - if (IsNPC()) - a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC - // update NPC stuff - auto new_pos = glm::vec3(m_Position.x + (a->force * std::cos(a->hit_heading) + m_Delta.x), - m_Position.y + (a->force * std::sin(a->hit_heading) + m_Delta.y), m_Position.z); - if (position_update_melee_push_timer.Check() && zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. - if (IsNPC()) { - // Is this adequate? - - Teleport(new_pos); - SendPositionUpdate(); - } + if (IsNPC()) { + if (attacker->IsNPC()) + a->force = 0.0f; // 2013 change that disabled NPC vs NPC push + else + a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC } - else { - a->force = 0.0f; // we couldn't move there, so lets not + // update NPC stuff + if (a->force != 0.0f) { + auto new_pos = glm::vec3( + m_Position.x + (a->force * std::cos(a->hit_heading) + m_Delta.x), + m_Position.y + (a->force * std::sin(a->hit_heading) + m_Delta.y), m_Position.z); + if (position_update_melee_push_timer.Check() && zone->zonemap && + zone->zonemap->CheckLoS( + glm::vec3(m_Position), + new_pos)) { // If we have LoS on the new loc it should be reachable. + if (IsNPC()) { + // Is this adequate? + + Teleport(new_pos); + SendPositionUpdate(); + } + } else { + a->force = 0.0f; // we couldn't move there, so lets not + } } } From 28cb11f52146659c24dc9baaefe115aad211e7fd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 14:43:37 -0500 Subject: [PATCH 26/59] Implement SE_TossUP -- actually a DMG SPA This just causes damage to NPCs only --- zone/spell_effects.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 30cf10450..5f151e487 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2075,6 +2075,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Toss Up: %d", effect_value); #endif + if (IsNPC()) { + Damage(caster, std::abs(effect_value), spell_id, spell.skill, false, buffslot, false); + } break; } From 1beb221337fbdb6068e9712cb18d33b74d0a1a5f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 16:15:57 -0500 Subject: [PATCH 27/59] Pets seem to run until within 35 units of you This was at 75 before, testing on live puts this closer to 35 --- zone/mob_ai.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index a7d1c2cb4..4d213dc7d 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -929,7 +929,7 @@ void Client::AI_Process() SendPositionUpdate(); // this shouldn't happen a lot (and hard to make it) so lets not rate limit } else if (dist >= 400) { // >=20 if (AI_movement_timer->Check()) { - int nspeed = (dist >= 5625 ? GetRunspeed() : GetWalkspeed()); // >= 75 + int nspeed = (dist >= 1225 ? GetRunspeed() : GetWalkspeed()); // >= 35 animation = nspeed; nspeed *= 2; SetCurrentSpeed(nspeed); @@ -1425,7 +1425,7 @@ void Mob::AI_Process() { if (dist >= 400 || distz > 100) { int speed = GetWalkspeed(); - if (dist >= 5625) + if (dist >= 1225) // 35 speed = GetRunspeed(); if (dist >= 202500 || distz > 100) // dist >= 450 From 3cb7b362c8adbd14f59fd9ad4c9174361daf2a2e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 16:42:46 -0500 Subject: [PATCH 28/59] Only rate limit NPC push Clients are fine --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 457518c67..1ae42b168 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3626,7 +3626,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const auto new_pos = glm::vec3( m_Position.x + (a->force * std::cos(a->hit_heading) + m_Delta.x), m_Position.y + (a->force * std::sin(a->hit_heading) + m_Delta.y), m_Position.z); - if (position_update_melee_push_timer.Check() && zone->zonemap && + if ((IsNPC() && position_update_melee_push_timer.Check()) && zone->zonemap && zone->zonemap->CheckLoS( glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. From 9e4f728f212c9fbd07c8e3a2190743f71ebc7370 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Mar 2018 18:10:06 -0500 Subject: [PATCH 29/59] Spell push values can be negative --- zone/spells.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 0e643077e..d5da2a34f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2663,7 +2663,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->effect_flag = 4; - if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) { if(IsClient()) { @@ -3937,7 +3937,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // the complete sequence is 2 actions and 1 damage message action->effect_flag = 0x04; // this is a success flag - if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) { if(spelltar->IsClient()) { From a5c32b63b7bfa648337b431a8e5c5ffe95d02eb7 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 2 Mar 2018 19:05:16 -0500 Subject: [PATCH 30/59] Make old style poison work much closer to live. --- zone/client_packet.cpp | 68 ++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d336f7f0d..c647bde78 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -72,6 +72,9 @@ extern PetitionList petition_list; extern EntityList entity_list; typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); +#define MIN(x,y) ((x) ConnectingOpcodes; //Use a static array for connected, for speed @@ -2861,41 +2864,57 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) DumpPacket(app); return; } + uint32 ApplyPoisonSuccessResult = 0; ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; const EQEmu::ItemInstance* PrimaryWeapon = GetInv().GetItem(EQEmu::inventory::slotPrimary); const EQEmu::ItemInstance* SecondaryWeapon = GetInv().GetItem(EQEmu::inventory::slotSecondary); const EQEmu::ItemInstance* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; + const EQEmu::ItemData* poison=PoisonItemInstance->GetItem(); + const EQEmu::ItemData* primary=nullptr; + const EQEmu::ItemData* secondary=nullptr; + bool IsPoison = PoisonItemInstance && + (poison->ItemType == EQEmu::item::ItemTypePoison); - bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == EQEmu::item::ItemTypePoison); - - if (!IsPoison) - { - Log(Logs::Detail, Logs::Spells, "Item used to cast spell effect from a poison item was missing from inventory slot %d " - "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); - - Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); + if (PrimaryWeapon) { + primary=PrimaryWeapon->GetItem(); } - else if (GetClass() == ROGUE) - { - if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing) || - (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing)) { - float SuccessChance = (GetSkill(EQEmu::skills::SkillApplyPoison) + GetLevel()) / 400.0f; + + if (SecondaryWeapon) { + secondary=SecondaryWeapon->GetItem(); + } + + if (IsPoison && GetClass() == ROGUE) { + + // Live always checks for skillup, even when poison is too high + CheckIncreaseSkill(EQEmu::skills::SkillApplyPoison, nullptr, 10); + + if (poison->Proc.Level2 > GetLevel()) { + // Poison is too high to apply. + Message_StringID(clientMessageTradeskill, POISON_TOO_HIGH); + } + else if ((primary && + primary->ItemType == EQEmu::item::ItemType1HPiercing) || + (secondary && + secondary->ItemType == EQEmu::item::ItemType1HPiercing)) { + double ChanceRoll = zone->random.Real(0, 1); - CheckIncreaseSkill(EQEmu::skills::SkillApplyPoison, nullptr, 10); + // Poisons that use this skill (old world poisons) almost + // never fail to apply. I did 25 applies of a trivial 120+ + // poison with an apply skill of 48 and they all worked. + // Poisons that don't proc until a level higher than the + // rogue simply won't apply at all, as done above now. - if (ChanceRoll < SuccessChance) { + if (ChanceRoll < .90) { ApplyPoisonSuccessResult = 1; - // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. - // My thinking was that DEX should be apart of the calculation. - AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103); + AddProcToWeapon(poison->Proc.Effect, false, + (GetDEX() / 100) + 103); } - - DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); - - Log(Logs::General, Logs::None, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); } + + // Live always deletes the item, success or failure. Even if too high. + DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); } auto outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); @@ -10790,6 +10809,11 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) case POPUPID_UPDATE_SHOWSTATSWINDOW: if (GetTarget() && GetTarget()->IsClient()) GetTarget()->CastToClient()->SendStatsWindow(this, true); + else if (GetTarget() && GetTarget()->IsPet()) + { + Pet *pet = (Pet *) GetTarget(); + pet->ShowInventory(this); + } else SendStatsWindow(this, true); return; From bb3596d24560e97a77a2e156ac28b64d2d2efd92 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 2 Mar 2018 19:14:48 -0500 Subject: [PATCH 31/59] Adjusted chances for apply slightly. --- zone/client_packet.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c647bde78..6974f3861 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2903,10 +2903,13 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // Poisons that use this skill (old world poisons) almost // never fail to apply. I did 25 applies of a trivial 120+ // poison with an apply skill of 48 and they all worked. + // Also did 25 straight poisons at apply skill 248 for very + // high end and they never failed. + // Apply poison ranging from 1-9, 28/30 worked for a level 18.. // Poisons that don't proc until a level higher than the - // rogue simply won't apply at all, as done above now. + // rogue simply won't apply at all, no skill check done. - if (ChanceRoll < .90) { + if (ChanceRoll < (.9 + GetLevel()/1000) { ApplyPoisonSuccessResult = 1; AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103); @@ -10809,11 +10812,6 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) case POPUPID_UPDATE_SHOWSTATSWINDOW: if (GetTarget() && GetTarget()->IsClient()) GetTarget()->CastToClient()->SendStatsWindow(this, true); - else if (GetTarget() && GetTarget()->IsPet()) - { - Pet *pet = (Pet *) GetTarget(); - pet->ShowInventory(this); - } else SendStatsWindow(this, true); return; From 66ef95b9c97c95a06192c67cc152d3666b775780 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 2 Mar 2018 19:26:41 -0500 Subject: [PATCH 32/59] Missed a header file for poison commit --- zone/string_ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/string_ids.h b/zone/string_ids.h index 997858bed..7786d12dc 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -120,6 +120,7 @@ #define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap. #define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. #define PICK_LORE 379 //You cannot pick up a lore item you already possess. +#define POISON_TOO_HIGH 382 // This poison is too high level for you to apply. #define CONSENT_DENIED 390 //You do not have consent to summon that corpse. #define DISCIPLINE_RDY 393 //You are ready to use a new discipline now. #define CONSENT_INVALID_NAME 397 //Not a valid consent name. From 6fb3c66fe5fc717e7a70a95a1936a859ca246449 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 2 Mar 2018 20:08:06 -0500 Subject: [PATCH 33/59] Fixed last minute math change typo. --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6974f3861..7e38d6363 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2909,7 +2909,7 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // Poisons that don't proc until a level higher than the // rogue simply won't apply at all, no skill check done. - if (ChanceRoll < (.9 + GetLevel()/1000) { + if (ChanceRoll < (.9 + GetLevel()/1000)) { ApplyPoisonSuccessResult = 1; AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103); From b6679446129b5891164b8cac756412d7d0c5e2cd Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 2 Mar 2018 20:13:24 -0500 Subject: [PATCH 34/59] Removed unneeded macro --- zone/client_packet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 7e38d6363..34c1387b9 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -72,8 +72,6 @@ extern PetitionList petition_list; extern EntityList entity_list; typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); -#define MIN(x,y) ((x) ConnectingOpcodes; From ee4a79616f28c50f030902ad53a43276c6049a34 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 3 Mar 2018 00:59:44 -0500 Subject: [PATCH 35/59] Lets can't a reference in resends --- common/net/daybreak_connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index 095fab525..d32f9f678 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -165,7 +165,7 @@ void EQ::Net::DaybreakConnectionManager::ProcessResend() { auto iter = m_connections.begin(); while (iter != m_connections.end()) { - auto connection = iter->second; + auto &connection = iter->second; auto status = connection->m_status; switch (status) @@ -1397,4 +1397,4 @@ EQ::Net::SequenceOrder EQ::Net::DaybreakConnection::CompareSequence(uint16_t exp } return SequencePast; -} \ No newline at end of file +} From 5aab187e172744527a6d40e5e48c154ab36993fd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 3 Mar 2018 13:10:32 -0500 Subject: [PATCH 36/59] Fix missile heading --- zone/special_attacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 03b144b5a..7c91c97a7 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1443,7 +1443,7 @@ void Mob::SendItemAnimation(Mob *to, const EQEmu::ItemData *item, EQEmu::skills: //these angle and tilt used together seem to make the arrow/knife throw as straight as I can make it - as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2; + as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY()); as->tilt = 125; as->arc = 50; From b63923264db32f01b050224ff042e854660fb1cf Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 3 Mar 2018 22:21:06 -0500 Subject: [PATCH 37/59] Fix memory leak --- zone/aura.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/aura.cpp b/zone/aura.cpp index 8c54b75ee..08600811a 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -616,6 +616,7 @@ bool Aura::Process() it = spawned_for.erase(it); } } + safe_delete(app); } // TODO: waypoints? From 690d8f9155045f6589dcc7ac0c6acd78bd4fc1b0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 3 Mar 2018 22:37:06 -0500 Subject: [PATCH 38/59] Fix Conditional jump or move depends on uninitialised value(s) --- zone/client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/client.cpp b/zone/client.cpp index 662c37071..16157c807 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -337,6 +337,7 @@ Client::Client(EQStreamInterface* ieqs) m_pp.InnateSkills[i] = InnateDisabled; temp_pvp = false; + is_client_moving = false; AI_Init(); } From 35c4867334a9e69d3d7693e26669fbf395d7c642 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 4 Mar 2018 02:19:12 -0500 Subject: [PATCH 39/59] Add the Fast trig functions that work with EQ headings This should match pretty close to the clients LUTs Also fixed a bug with push --- zone/CMakeLists.txt | 2 ++ zone/attack.cpp | 8 +++++--- zone/fastmath.cpp | 32 ++++++++++++++++++++++++++++++++ zone/fastmath.h | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 zone/fastmath.cpp create mode 100644 zone/fastmath.h diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 49644cfcf..b10ab32a0 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -28,6 +28,7 @@ SET(zone_sources encounter.cpp entity.cpp exp.cpp + fastmath.cpp fearpath.cpp forage.cpp groups.cpp @@ -158,6 +159,7 @@ SET(zone_headers entity.h errmsg.h event_codes.h + fastmath.h forage.h global_loot_manager.h groups.h diff --git a/zone/attack.cpp b/zone/attack.cpp index 1ae42b168..90a774550 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "worldserver.h" #include "zone.h" #include "lua_parser.h" +#include "fastmath.h" #include #include @@ -43,6 +44,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA extern QueryServ* QServ; extern WorldServer worldserver; +extern FastMath g_Math; #ifdef _WINDOWS #define snprintf _snprintf @@ -3624,9 +3626,9 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const // update NPC stuff if (a->force != 0.0f) { auto new_pos = glm::vec3( - m_Position.x + (a->force * std::cos(a->hit_heading) + m_Delta.x), - m_Position.y + (a->force * std::sin(a->hit_heading) + m_Delta.y), m_Position.z); - if ((IsNPC() && position_update_melee_push_timer.Check()) && zone->zonemap && + m_Position.x + (a->force * g_Math.FastSin(a->hit_heading) + m_Delta.x), + m_Position.y + (a->force * g_Math.FastCos(a->hit_heading) + m_Delta.y), m_Position.z); + if ((!IsNPC() || position_update_melee_push_timer.Check()) && zone->zonemap && zone->zonemap->CheckLoS( glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. diff --git a/zone/fastmath.cpp b/zone/fastmath.cpp new file mode 100644 index 000000000..fb2cef193 --- /dev/null +++ b/zone/fastmath.cpp @@ -0,0 +1,32 @@ +#include +#include "fastmath.h" + +FastMath g_Math; + +// This should match EQ's sin/cos LUTs +// Some values didn't match on linux, but they were the "same" float :P +FastMath::FastMath() +{ + int ci = 0; + int si = 128; + float res; + do { + res = std::cos(static_cast(ci) * M_PI * 2 / 512); + lut_cos[ci] = res; + if (si == 512) + si = 0; + lut_sin[si] = res; + ++ci; + ++si; + } while (ci < 512); + + lut_sin[0] = 0.0f; + lut_sin[128] = 1.0f; + lut_sin[256] = 0.0f; + lut_sin[384] = -1.0f; + + lut_cos[0] = 1.0f; + lut_cos[128] = 0.0f; + lut_cos[384] = 0.0f; +} + diff --git a/zone/fastmath.h b/zone/fastmath.h new file mode 100644 index 000000000..083198e48 --- /dev/null +++ b/zone/fastmath.h @@ -0,0 +1,18 @@ +#ifndef FASTMATH_H +#define FASTMATH_H + +class FastMath +{ +private: + float lut_cos[512]; + float lut_sin[512]; + +public: + FastMath(); + + inline float FastSin(float a) { return lut_sin[static_cast(a) & 0x1ff]; } + inline float FastCos(float a) { return lut_cos[static_cast(a) & 0x1ff]; } + +}; + +#endif /* !FASTMATH_H */ From 525db1819dfea8a1d7f8a4bd631ef00e38a23709 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 4 Mar 2018 13:54:04 -0500 Subject: [PATCH 40/59] Fix VS compilation hopefully --- zone/fastmath.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/fastmath.cpp b/zone/fastmath.cpp index fb2cef193..0da87dc87 100644 --- a/zone/fastmath.cpp +++ b/zone/fastmath.cpp @@ -1,3 +1,6 @@ +#if defined(_MSC_VER) +#define _USE_MATH_DEFINES +#endif #include #include "fastmath.h" From e5e779c0643e769b5d3d4bd34712308e1db0fac7 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 4 Mar 2018 21:38:17 -0500 Subject: [PATCH 41/59] Updated UCS versioning code - update your *.conf files --- changelog.txt | 7 ++ common/emu_oplist.h | 1 + common/emu_versions.h | 14 ++++ common/ruletypes.h | 3 - common/servertalk.h | 26 +++--- common/shareddb.cpp | 18 ++++ common/shareddb.h | 1 + ucs/clientlist.cpp | 153 +++++++++++----------------------- ucs/clientlist.h | 14 ---- ucs/ucs.cpp | 7 -- ucs/worldserver.cpp | 54 ------------ ucs/worldserver.h | 7 -- utils/patches/patch_RoF.conf | 1 + utils/patches/patch_RoF2.conf | 1 + utils/patches/patch_SoD.conf | 1 + utils/patches/patch_UF.conf | 1 + world/client.cpp | 112 +++++++++++++++---------- world/net.cpp | 4 + world/ucs.cpp | 9 -- world/zonelist.cpp | 11 +++ world/zonelist.h | 3 +- world/zoneserver.cpp | 20 ++++- zone/client_packet.cpp | 81 +++++++++++++++++- zone/client_packet.h | 1 + zone/worldserver.cpp | 65 +-------------- zone/zone.cpp | 31 ++++++- zone/zone.h | 9 +- zone/zonedb.cpp | 11 ++- 28 files changed, 338 insertions(+), 328 deletions(-) diff --git a/changelog.txt b/changelog.txt index 12a9fc76a..360f61082 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/04/2018 == +Uleat: Updated UCS versioning + - SoF and higher clients have a new opcode identified (update your *.conf files) + - Rework of previous ucs connectivity code + - Unrelated: Zone::weatherSend() now takes an optional parameter for singular updates (as in client entering zone) + -- prior to this, every client already in-zone received a weather update packet whenever a new client zoned in + == 02/18/2018 == Uleat: Bug reporting fix and overhaul. - Fixed bug reporting for SoD+ clients diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 89e0126a8..5078cca68 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -393,6 +393,7 @@ N(OP_PVPLeaderBoardReply), N(OP_PVPLeaderBoardRequest), N(OP_PVPStats), N(OP_QueryResponseThing), +N(OP_QueryUCSServerStatus), N(OP_RaidInvite), N(OP_RaidJoin), N(OP_RaidUpdate), diff --git a/common/emu_versions.h b/common/emu_versions.h index 9ab5becde..a5f3d0183 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -121,6 +121,20 @@ namespace EQEmu ClientVersion ConvertOfflinePCMobVersionToClientVersion(MobVersion mob_version); MobVersion ConvertClientVersionToOfflinePCMobVersion(ClientVersion client_version); + + enum UCSVersion : char { + ucsUnknown = '\0', + ucsClient62Chat = 'A', + ucsClient62Mail = 'a', + ucsTitaniumChat = 'B', + ucsTitaniumMail = 'b', + ucsSoFCombined = 'C', + ucsSoDCombined = 'D', + ucsUFCombined = 'E', + ucsRoFCombined = 'F', + ucsRoF2Combined = 'G' + }; + } /*versions*/ } /*EQEmu*/ diff --git a/common/ruletypes.h b/common/ruletypes.h index f6c9f569b..d80996184 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -614,9 +614,6 @@ RULE_INT(Chat, IntervalDurationMS, 60000) RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000) RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit RULE_INT(Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. -RULE_INT(Chat, ExpireClientVersionRequests, 3) // time in seconds to keep current cv requests active -RULE_INT(Chat, ExpireClientVersionReplies, 30) // time in seconds to keep current cv replies active -RULE_INT(Chat, UCSBroadcastServerReadyDelay, 60) // time in seconds to delay broadcast `server ready` after start-up RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/common/servertalk.h b/common/servertalk.h index 82f39319f..e1d019fc4 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -190,9 +190,8 @@ #define ServerOP_ReloadLogs 0x4010 #define ServerOP_ReloadPerlExportSettings 0x4011 #define ServerOP_CZSetEntityVariableByClientName 0x4012 -#define ServerOP_UCSClientVersionRequest 0x4013 -#define ServerOP_UCSClientVersionReply 0x4014 -#define ServerOP_UCSBroadcastServerReady 0x4015 +#define ServerOP_UCSServerStatusRequest 0x4013 +#define ServerOP_UCSServerStatusReply 0x4014 /* Query Server OP Codes */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 @@ -1281,18 +1280,15 @@ struct ServerRequestTellQueue_Struct { char name[64]; }; -struct UCSClientVersionRequest_Struct { - uint32 character_id; -}; - -struct UCSClientVersionReply_Struct { - uint32 character_id; - EQEmu::versions::ClientVersion client_version; -}; - -struct UCSBroadcastServerReady_Struct { - char chat_prefix[128]; - char mail_prefix[128]; +struct UCSServerStatus_Struct { + uint8 available; // non-zero=true, 0=false + union { + struct { + uint16 port; + uint16 unused; + }; + uint32 timestamp; + }; }; #pragma pack() diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 4a82c32da..a4c6e54d1 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -127,6 +127,24 @@ void SharedDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) } +std::string SharedDatabase::GetMailKey(int CharID, bool key_only) +{ + std::string query = StringFormat("SELECT `mailkey` FROM `character_data` WHERE `id`='%i' LIMIT 1", CharID); + auto results = QueryDatabase(query); + if (!results.Success()) { + Log(Logs::Detail, Logs::MySQLError, "Error retrieving mailkey from database: %s", results.ErrorMessage().c_str()); + return std::string(); + } + + auto row = results.begin(); + std::string mail_key = row[0]; + + if (mail_key.length() > 8 && key_only) + return mail_key.substr(8); + else + return mail_key; +} + bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) { // Delete cursor items diff --git a/common/shareddb.h b/common/shareddb.h index 171c2b8c6..bbd8dc9a0 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -73,6 +73,7 @@ class SharedDatabase : public Database bool GetCommandSettings(std::map>> &command_settings); uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); void SetMailKey(int CharID, int IPAddress, int MailKey); + std::string GetMailKey(int CharID, bool key_only = false); /* Character InventoryProfile diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index e5076a150..130914952 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -512,7 +512,6 @@ Client::Client(std::shared_ptr eqs) { AccountGrabUpdateTimer = new Timer(60000); //check every minute GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); - RawConnectionType = '\0'; TypeOfConnection = ConnectionTypeUnknown; ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; @@ -645,10 +644,6 @@ void Clientlist::Process() database.GetAccountStatus((*it)); - // give world packet a chance to arrive and be processed - if ((*it)->GetCharID()) - ClientVersionRequestQueue[(*it)->GetCharID()] = (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionRequests) * 1000)); - if ((*it)->GetConnectionType() == ConnectionTypeCombined) (*it)->SendFriends(); @@ -688,34 +683,8 @@ void Clientlist::Process() continue; } - // initiate request if we don't already have a reply from 'world enter' (ucs crash recovery protocol) - if ((*it)->GetClientVersion() == EQEmu::versions::ClientVersion::Unknown) { - if (!CheckForClientVersionReply((*it))) - RequestClientVersion((*it)->GetCharID()); - } - ++it; } - - // delete expired replies - auto repiter = ClientVersionReplyQueue.begin(); - while (repiter != ClientVersionReplyQueue.end()) { - if ((*repiter).second.second <= Timer::GetCurrentTime()) { - repiter = ClientVersionReplyQueue.erase(repiter); - continue; - } - ++repiter; - } - - // delete expired requests - auto reqiter = ClientVersionRequestQueue.begin(); - while (reqiter != ClientVersionRequestQueue.end()) { - if ((*reqiter).second <= Timer::GetCurrentTime()) { - reqiter = ClientVersionRequestQueue.erase(reqiter); - continue; - } - ++reqiter; - } } void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) @@ -873,55 +842,6 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) } } -void Clientlist::RequestClientVersion(uint32 character_id) { - if (!character_id) - return; - - if (ClientVersionRequestQueue.find(character_id) != ClientVersionRequestQueue.end()) { - if (ClientVersionRequestQueue[character_id] > Timer::GetCurrentTime()) - return; - } - - if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { - Log(Logs::Detail, Logs::UCS_Server, "Requesting ClientVersion reply for character id: %u", - character_id); - } - ClientVersionRequestIDs.push_back(character_id); - ClientVersionRequestQueue[character_id] = (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionRequests) * 1000)); -} - -bool Clientlist::QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version) { - if (!character_id) - return true; - if (client_version < EQEmu::versions::ClientVersion::Titanium || client_version > EQEmu::versions::ClientVersion::RoF2) - return false; - - if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { - Log(Logs::Detail, Logs::UCS_Server, "Queueing ClientVersion %u reply for character id: %u", - static_cast(client_version), character_id); - } - ClientVersionReplyQueue[character_id] = cvt_pair(client_version, (Timer::GetCurrentTime() + (RuleI(Chat, ExpireClientVersionReplies) * 1000))); - ClientVersionRequestQueue.erase(character_id); - - return true; -} - -bool Clientlist::CheckForClientVersionReply(Client* c) { - if (!c) - return true; - if (ClientVersionReplyQueue.find(c->GetCharID()) == ClientVersionReplyQueue.end()) - return false; - - if (LogSys.log_settings[Logs::UCS_Server].is_category_enabled) { - Log(Logs::General, Logs::UCS_Server, "Registering ClientVersion %s for stream %s:%u", - EQEmu::versions::ClientVersionName(ClientVersionReplyQueue[c->GetCharID()].first), c->ClientStream->GetRemoteAddr().c_str(), c->ClientStream->GetRemotePort()); - } - c->SetClientVersion(ClientVersionReplyQueue[c->GetCharID()].first); - ClientVersionReplyQueue.erase(c->GetCharID()); - - return true; -} - void Clientlist::CloseAllConnections() { @@ -2214,39 +2134,64 @@ void Client::AccountUpdate() void Client::SetConnectionType(char c) { - RawConnectionType = c; - switch (c) { - case 'S': - { - TypeOfConnection = ConnectionTypeCombined; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoF/SoD)"); - break; - } - case 'U': - { - TypeOfConnection = ConnectionTypeCombined; - UnderfootOrLater = true; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (Underfoot+)"); - break; - } - case 'M': - { - TypeOfConnection = ConnectionTypeMail; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Mail (6.2 or Titanium client)"); - break; - } - case 'C': + case EQEmu::versions::ucsTitaniumChat: { TypeOfConnection = ConnectionTypeChat; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Chat (6.2 or Titanium client)"); + ClientVersion_ = EQEmu::versions::ClientVersion::Titanium; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Chat (Titanium)"); + break; + } + case EQEmu::versions::ucsTitaniumMail: + { + TypeOfConnection = ConnectionTypeMail; + ClientVersion_ = EQEmu::versions::ClientVersion::Titanium; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Mail (Titanium)"); + break; + } + case EQEmu::versions::ucsSoFCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::SoF; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoF)"); + break; + } + case EQEmu::versions::ucsSoDCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::SoD; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoD)"); + break; + } + case EQEmu::versions::ucsUFCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::UF; + UnderfootOrLater = true; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (Underfoot)"); + break; + } + case EQEmu::versions::ucsRoFCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::RoF; + UnderfootOrLater = true; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (RoF)"); + break; + } + case EQEmu::versions::ucsRoF2Combined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::RoF2; + UnderfootOrLater = true; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (RoF2)"); break; } default: { - RawConnectionType = '\0'; TypeOfConnection = ConnectionTypeUnknown; + ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; Log(Logs::Detail, Logs::UCS_Server, "Connection type is unknown."); } } diff --git a/ucs/clientlist.h b/ucs/clientlist.h index f84bdc8a2..6021c5c0e 100644 --- a/ucs/clientlist.h +++ b/ucs/clientlist.h @@ -140,10 +140,8 @@ public: int GetMailBoxNumber() { return CurrentMailBox; } int GetMailBoxNumber(std::string CharacterName); - char GetRawConnectionType() { return RawConnectionType; } void SetConnectionType(char c); ConnectionType GetConnectionType() { return TypeOfConnection; } - void SetClientVersion(EQEmu::versions::ClientVersion client_version) { ClientVersion_ = client_version; } EQEmu::versions::ClientVersion GetClientVersion() { return ClientVersion_; } inline bool IsMailConnection() { return (TypeOfConnection == ConnectionTypeMail) || (TypeOfConnection == ConnectionTypeCombined); } @@ -173,7 +171,6 @@ private: int AttemptedMessages; bool ForceDisconnect; - char RawConnectionType; ConnectionType TypeOfConnection; EQEmu::versions::ClientVersion ClientVersion_; bool UnderfootOrLater; @@ -190,22 +187,11 @@ public: Client *IsCharacterOnline(std::string CharacterName); void ProcessOPMailCommand(Client *c, std::string CommandString); - std::list ClientVersionRequestIDs; - - void RequestClientVersion(uint32 character_id); - bool QueueClientVersionReply(uint32 character_id, EQEmu::versions::ClientVersion client_version); - bool CheckForClientVersionReply(Client* c); - private: - typedef std::pair cvt_pair; - EQ::Net::EQStreamManager *chatsf; std::list ClientChatConnections; - std::map ClientVersionRequestQueue; - std::map ClientVersionReplyQueue; - OpcodeManager *ChatOpMgr; }; diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index b59effd03..b9e7f41b4 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -140,19 +140,12 @@ int main() { worldserver = new WorldServer; - worldserver->ActivateBroadcastServerReadyTimer(); - while(RunLoops) { - // this triggers clients to 'reconnect' to ucs after server crash - if (worldserver->HasBroadcastServerReadyTimer()) - worldserver->ProcessBroadcastServerReady(); Timer::SetCurrentTime(); g_Clientlist->Process(); - worldserver->ProcessClientVersionRequests(g_Clientlist->ClientVersionRequestIDs); - if(ChannelListProcessTimer.Check()) ChannelList->Process(); diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index 48ec03dca..b3df5d99d 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -50,8 +50,6 @@ WorldServer::WorldServer() { m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "UCS", Config->SharedKey)); m_connection->OnMessage(std::bind(&WorldServer::ProcessMessage, this, std::placeholders::_1, std::placeholders::_2)); - - m_bsr_timer = nullptr; } WorldServer::~WorldServer() @@ -140,59 +138,7 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) std::string()); break; } - - case ServerOP_UCSClientVersionReply: - { - UCSClientVersionReply_Struct* cvr = (UCSClientVersionReply_Struct*)pack->pBuffer; - g_Clientlist->QueueClientVersionReply(cvr->character_id, cvr->client_version); - break; } - } -} - -void WorldServer::ProcessClientVersionRequests(std::list& id_list) { - UCSClientVersionRequest_Struct cvr; - EQ::Net::DynamicPacket dp_cvr; - for (auto iter : id_list) { - cvr.character_id = iter; - dp_cvr.PutData(0, &cvr, sizeof(cvr)); - m_connection->Send(ServerOP_UCSClientVersionRequest, dp_cvr); - } - id_list.clear(); -} - -void WorldServer::ProcessBroadcastServerReady() { - if (m_bsr_timer && (*m_bsr_timer) <= Timer::GetCurrentTime()) { - UCSBroadcastServerReady_Struct bsr; - memset(&bsr, 0, sizeof(UCSBroadcastServerReady_Struct)); - - sprintf(bsr.chat_prefix, "%s,%i,%s.", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str() - ); - sprintf(bsr.mail_prefix, "%s,%i,%s.", - Config->ChatHost.c_str(), - Config->MailPort, - Config->ShortName.c_str() - ); - - EQ::Net::DynamicPacket dp_bsr; - dp_bsr.PutData(0, &bsr, sizeof(UCSBroadcastServerReady_Struct)); - m_connection->Send(ServerOP_UCSBroadcastServerReady, dp_bsr); - - safe_delete(m_bsr_timer); - } -} - -void WorldServer::ActivateBroadcastServerReadyTimer() { - safe_delete(m_bsr_timer); - m_bsr_timer = new uint32; - - // clients do not drop their connection to ucs immediately... - // it can take upwards of 60 seconds to process the drop - // and clients will not re-connect to ucs until that occurs - *m_bsr_timer = (Timer::GetCurrentTime() + (RuleI(Chat, UCSBroadcastServerReadyDelay) * 1000)); } void Client45ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { diff --git a/ucs/worldserver.h b/ucs/worldserver.h index 66ed0ec32..aad68e085 100644 --- a/ucs/worldserver.h +++ b/ucs/worldserver.h @@ -29,14 +29,7 @@ public: ~WorldServer(); void ProcessMessage(uint16 opcode, EQ::Net::Packet &); - void ProcessClientVersionRequests(std::list& id_list); - - void ProcessBroadcastServerReady(); - bool HasBroadcastServerReadyTimer() { return (m_bsr_timer != nullptr); } - void ActivateBroadcastServerReadyTimer(); - private: - uint32* m_bsr_timer; std::unique_ptr m_connection; }; diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 074d5751d..b348ea1ad 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -165,6 +165,7 @@ OP_GMNameChange=0x3077 # Was 0x4434 OP_GMLastName=0x4dd7 # Was 0x3077 # Misc Opcodes +OP_QueryUCSServerStatus=0x6964 OP_InspectRequest=0x23f1 OP_InspectAnswer=0x5794 OP_InspectMessageUpdate=0x3064 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index dd58b3c11..9bd507bc1 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -164,6 +164,7 @@ OP_GMNameChange=0x035f OP_GMLastName=0x46ce # Misc Opcodes +OP_QueryUCSServerStatus=0x398f OP_InspectRequest=0x57bc OP_InspectAnswer=0x71ac OP_InspectMessageUpdate=0x4d25 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 5913670ae..ab8267a8a 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -165,6 +165,7 @@ OP_GMKill=0x6685 # C OP_GMNameChange=0x565d # C OP_GMLastName=0x3563 # C +OP_QueryUCSServerStatus=0x4036 OP_InspectAnswer=0x4938 # C OP_Action2=0x7e4d # C OP_Damage? OP_BeginCast=0x0d5a # C diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index a96b4208a..c08edbca3 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -168,6 +168,7 @@ OP_GMKill=0x799c # C OP_GMNameChange=0x0f48 # C OP_GMLastName=0x7bfb # C +OP_QueryUCSServerStatus=0x4481 OP_InspectAnswer=0x0c2b # C OP_BeginCast=0x0d5a # C OP_ColoredText=0x71bf # C diff --git a/world/client.cpp b/world/client.cpp index d904e1d2d..d3b74ae3f 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -84,6 +84,7 @@ extern ClientList client_list; extern EQEmu::Random emu_random; extern uint32 numclients; extern volatile bool RunLoops; +extern volatile bool UCSServerAvailable_; Client::Client(EQStreamInterface* ieqs) : autobootup_timeout(RuleI(World, ZoneAutobootTimeoutMS)), @@ -890,53 +891,84 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { } QueuePacket(outapp); safe_delete(outapp); - + + // set mailkey - used for duration of character session int MailKey = emu_random.Int(1, INT_MAX); database.SetMailKey(charid, GetIP(), MailKey); + if (UCSServerAvailable_) { + const WorldConfig *Config = WorldConfig::get(); + std::string buffer; - char ConnectionType; + EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown; - if (m_ClientVersionBit & EQEmu::versions::bit_UFAndLater) - ConnectionType = 'U'; - else if (m_ClientVersionBit & EQEmu::versions::bit_SoFAndLater) - ConnectionType = 'S'; - else - ConnectionType = 'C'; + // chat server packet + switch (GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumChat; + break; + case EQEmu::versions::ClientVersion::SoF: + ConnectionType = EQEmu::versions::ucsSoFCombined; + break; + case EQEmu::versions::ClientVersion::SoD: + ConnectionType = EQEmu::versions::ucsSoDCombined; + break; + case EQEmu::versions::ClientVersion::UF: + ConnectionType = EQEmu::versions::ucsUFCombined; + break; + case EQEmu::versions::ClientVersion::RoF: + ConnectionType = EQEmu::versions::ucsRoFCombined; + break; + case EQEmu::versions::ClientVersion::RoF2: + ConnectionType = EQEmu::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQEmu::versions::ucsUnknown; + break; + } - auto outapp2 = new EQApplicationPacket(OP_SetChatServer); - char buffer[112]; + buffer = StringFormat("%s,%i,%s.%s,%c%08X", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + GetCharName(), + ConnectionType, + MailKey + ); - const WorldConfig *Config = WorldConfig::get(); + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); + QueuePacket(outapp); + safe_delete(outapp); - outapp2 = new EQApplicationPacket(OP_SetChatServer2); + // mail server packet + switch (GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } - if (m_ClientVersionBit & EQEmu::versions::bit_TitaniumAndEarlier) - ConnectionType = 'M'; + buffer = StringFormat("%s,%i,%s.%s,%c%08X", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + GetCharName(), + ConnectionType, + MailKey + ); - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + QueuePacket(outapp); + safe_delete(outapp); + } EnterWorld(); @@ -1215,14 +1247,6 @@ void Client::EnterWorld(bool TryBootup) { wtz->response = 0; zone_server->SendPacket(pack); delete pack; - - UCSClientVersionReply_Struct cvr; - cvr.character_id = GetCharID(); - cvr.client_version = GetClientVersion(); - EQ::Net::DynamicPacket dp_cvr; - dp_cvr.PutData(0, &cvr, sizeof(cvr)); - zone_server->HandleMessage(ServerOP_UCSClientVersionReply, dp_cvr); - } else { // if they havent seen character select screen, we can assume this is a zone // to zone movement, which should be preauthorized before they leave the previous zone diff --git a/world/net.cpp b/world/net.cpp index 033f470f7..c9a3f134c 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -462,6 +462,8 @@ int main(int argc, char** argv) { connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); UCSLink.SetConnection(connection); + + zoneserver_list.UpdateUCSServerAvailable(); }); server_connection->OnConnectionRemoved("UCS", [](std::shared_ptr connection) { @@ -469,6 +471,8 @@ int main(int argc, char** argv) { connection->GetUUID()); UCSLink.SetConnection(nullptr); + + zoneserver_list.UpdateUCSServerAvailable(false); }); server_connection->OnConnectionIdentified("WebInterface", [](std::shared_ptr connection) { diff --git a/world/ucs.cpp b/world/ucs.cpp index 76cba7e4b..b745005d4 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -2,14 +2,11 @@ #include "../common/eqemu_logsys.h" #include "ucs.h" #include "world_config.h" -#include "zonelist.h" #include "../common/misc_functions.h" #include "../common/md5.h" #include "../common/packet_dump.h" -extern ZSList zoneserver_list; - UCSConnection::UCSConnection() { Stream = 0; @@ -52,12 +49,6 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) Log(Logs::Detail, Logs::UCS_Server, "Got authentication from UCS when they are already authenticated."); break; } - case ServerOP_UCSBroadcastServerReady: - case ServerOP_UCSClientVersionRequest: - { - zoneserver_list.SendPacket(pack); - break; - } default: { Log(Logs::Detail, Logs::UCS_Server, "Unknown ServerOPcode from UCS 0x%04x, size %d", opcode, pack->size); diff --git a/world/zonelist.cpp b/world/zonelist.cpp index a1f0a60ff..d86f727f6 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -32,6 +32,7 @@ extern uint32 numzones; extern bool holdzones; extern EQEmu::Random emu_random; extern WebInterfaceList web_interface; +volatile bool UCSServerAvailable_ = false; void CatchSignal(int sig_num); ZSList::ZSList() @@ -669,6 +670,16 @@ void ZSList::GetZoneIDList(std::vector &zones) { } } +void ZSList::UpdateUCSServerAvailable(bool ucss_available) { + UCSServerAvailable_ = ucss_available; + auto outapp = new ServerPacket(ServerOP_UCSServerStatusReply, sizeof(UCSServerStatus_Struct)); + auto ucsss = (UCSServerStatus_Struct*)outapp->pBuffer; + ucsss->available = (ucss_available ? 1 : 0); + ucsss->timestamp = Timer::GetCurrentTime(); + SendPacket(outapp); + safe_delete(outapp); +} + void ZSList::WorldShutDown(uint32 time, uint32 interval) { if (time > 0) { diff --git a/world/zonelist.h b/world/zonelist.h index e152e5046..9911b87e0 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -54,7 +54,8 @@ public: Timer* reminder; void NextGroupIDs(uint32 &start, uint32 &end); void SendLSZones(); - uint16 GetAvailableZonePort(); + uint16 GetAvailableZonePort(); + void UpdateUCSServerAvailable(bool ucss_available = true); int GetZoneCount(); void GetZoneIDList(std::vector &zones); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 061ee6b72..e685b280e 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -41,6 +41,7 @@ extern GroupLFPList LFPGroupList; extern ZSList zoneserver_list; extern LoginServerList loginserverlist; extern volatile bool RunLoops; +extern volatile bool UCSServerAvailable_; extern AdventureManager adventure_manager; extern UCSConnection UCSLink; extern QueryServConnection QSLink; @@ -1262,12 +1263,29 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } - case ServerOP_UCSClientVersionReply: case ServerOP_UCSMailMessage: { UCSLink.SendPacket(pack); break; } + + case ServerOP_UCSServerStatusRequest: + { + auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer; + auto zs = zoneserver_list.FindByPort(ucsss->port); + if (!zs) + break; + + auto outapp = new ServerPacket(ServerOP_UCSServerStatusReply, sizeof(UCSServerStatus_Struct)); + ucsss = (UCSServerStatus_Struct*)outapp->pBuffer; + ucsss->available = (UCSServerAvailable_ ? 1 : 0); + ucsss->timestamp = Timer::GetCurrentTime(); + zs->SendPacket(outapp); + safe_delete(outapp); + + break; + } + case ServerOP_QSSendQuery: case ServerOP_QueryServGeneric: case ServerOP_Speech: diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 843a2c282..fe8af2a98 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -316,6 +316,7 @@ void MapOpcodes() ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; + ConnectedOpcodes[OP_QueryUCSServerStatus] = &Client::Handle_OP_QueryUCSServerStatus; ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; @@ -792,7 +793,7 @@ void Client::CompleteConnect() } if (zone) - zone->weatherSend(); + zone->weatherSend(this); TotalKarma = database.GetKarma(AccountID()); SendDisciplineTimers(); @@ -11005,6 +11006,84 @@ void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) safe_delete(outapp); } +void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app) +{ + if (zone->IsUCSServerAvailable()) { + EQApplicationPacket* outapp = nullptr; + std::string buffer; + + std::string MailKey = database.GetMailKey(CharacterID(), true); + EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown; + + // chat server packet + switch (ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumChat; + break; + case EQEmu::versions::ClientVersion::SoF: + ConnectionType = EQEmu::versions::ucsSoFCombined; + break; + case EQEmu::versions::ClientVersion::SoD: + ConnectionType = EQEmu::versions::ucsSoDCombined; + break; + case EQEmu::versions::ClientVersion::UF: + ConnectionType = EQEmu::versions::ucsUFCombined; + break; + case EQEmu::versions::ClientVersion::RoF: + ConnectionType = EQEmu::versions::ucsRoFCombined; + break; + case EQEmu::versions::ClientVersion::RoF2: + ConnectionType = EQEmu::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQEmu::versions::ucsUnknown; + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + switch (ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + QueuePacket(outapp); + safe_delete(outapp); + } +} + void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { if (app->size < sizeof(RaidGeneral_Struct)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 1e7981ed9..4f9902599 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -228,6 +228,7 @@ void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); + void Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app); void Handle_OP_RaidCommand(const EQApplicationPacket *app); void Handle_OP_RandomReq(const EQApplicationPacket *app); void Handle_OP_ReadBook(const EQApplicationPacket *app); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 09e5aa223..053bebacf 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1814,69 +1814,10 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } - case ServerOP_UCSBroadcastServerReady: + case ServerOP_UCSServerStatusReply: { - UCSBroadcastServerReady_Struct* bsr = (UCSBroadcastServerReady_Struct*)pack->pBuffer; - EQApplicationPacket* outapp = nullptr; - std::string buffer; - - for (auto liter : entity_list.GetClientList()) { - auto c = liter.second; - if (!c) - continue; - - int MailKey = zone->random.Int(1, INT_MAX); - - database.SetMailKey(c->CharacterID(), c->GetIP(), MailKey); - - char ConnectionType; - - // chat server packet - if (c->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) - ConnectionType = 'U'; - else if (c->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater) - ConnectionType = 'S'; - else - ConnectionType = 'C'; - - buffer = bsr->chat_prefix; - buffer.append(StringFormat("%s,%c%08X", c->GetName(), ConnectionType, MailKey)); - - outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); - memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); - outapp->pBuffer[buffer.length()] = '\0'; - - c->QueuePacket(outapp); - safe_delete(outapp); - - // mail server packet - if (c->ClientVersionBit() & EQEmu::versions::bit_TitaniumAndEarlier) - ConnectionType = 'M'; - - buffer = bsr->mail_prefix; - buffer.append(StringFormat("%s,%c%08X", c->GetName(), ConnectionType, MailKey)); - - outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); - memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); - outapp->pBuffer[buffer.length()] = '\0'; - - c->QueuePacket(outapp); - safe_delete(outapp); - } - break; - } - case ServerOP_UCSClientVersionRequest: - { - UCSClientVersionRequest_Struct* cvreq = (UCSClientVersionRequest_Struct*)pack->pBuffer; - Client* c = entity_list.GetClientByCharID(cvreq->character_id); - if (c) { - UCSClientVersionReply_Struct cvrep; - cvrep.character_id = c->CharacterID(); - cvrep.client_version = c->ClientVersion(); - EQ::Net::DynamicPacket dp_cvrep; - dp_cvrep.PutData(0, &cvrep, sizeof(cvrep)); - worldserver.m_connection->Send(ServerOP_UCSClientVersionReply, dp_cvrep); - } + auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer; + zone->SetUCSServerAvailable((ucsss->available != 0), ucsss->timestamp); break; } case ServerOP_CZSetEntityVariableByNPCTypeID: diff --git a/zone/zone.cpp b/zone/zone.cpp index fce562c94..90d59e17e 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -146,6 +146,8 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { UpdateWindowTitle(); zone->GetTimeSync(); + zone->RequestUCSServerStatus(); + /* Set Logging */ LogSys.StartFileLogs(StringFormat("%s_version_%u_inst_id_%u_port_%u", zone->GetShortName(), zone->GetInstanceVersion(), zone->GetInstanceID(), ZoneConfig::get()->ZonePort)); @@ -847,6 +849,9 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) GuildBanks = new GuildBankManager; else GuildBanks = nullptr; + + m_ucss_available = false; + m_last_ucss_update = 0; } Zone::~Zone() { @@ -1863,14 +1868,17 @@ bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes) { return true; } -void Zone::weatherSend() +void Zone::weatherSend(Client* client) { auto outapp = new EQApplicationPacket(OP_Weather, 8); if(zone_weather>0) outapp->pBuffer[0] = zone_weather-1; if(zone_weather>0) outapp->pBuffer[4] = zone->weather_intensity; - entity_list.QueueClients(0, outapp); + if (client) + client->QueuePacket(outapp); + else + entity_list.QueueClients(0, outapp); safe_delete(outapp); } @@ -2336,3 +2344,22 @@ void Zone::UpdateHotzone() is_hotzone = atoi(row[0]) == 0 ? false: true; } +void Zone::RequestUCSServerStatus() { + auto outapp = new ServerPacket(ServerOP_UCSServerStatusRequest, sizeof(UCSServerStatus_Struct)); + auto ucsss = (UCSServerStatus_Struct*)outapp->pBuffer; + ucsss->available = 0; + ucsss->port = Config->ZonePort; + ucsss->unused = 0; + worldserver.SendPacket(outapp); + safe_delete(outapp); +} + +void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) { + if (m_last_ucss_update == update_timestamp && m_ucss_available != ucss_available) { + m_ucss_available = false; + RequestUCSServerStatus(); + return; + } + if (m_last_ucss_update < update_timestamp) + m_ucss_available = ucss_available; +} diff --git a/zone/zone.h b/zone/zone.h index 158d414a4..bbd070305 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -224,7 +224,7 @@ public: void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute); void SetTime(uint8 hour, uint8 minute, bool update_world = true); - void weatherSend(); + void weatherSend(Client* client = nullptr); bool CanBind() const { return(can_bind); } bool IsCity() const { return(is_city); } bool CanDoCombat() const { return(can_combat); } @@ -275,6 +275,10 @@ public: inline void ShowZoneGlobalLoot(Client *to) { m_global_loot.ShowZoneGlobalLoot(to); } inline void ShowNPCGlobalLoot(Client *to, NPC *who) { m_global_loot.ShowNPCGlobalLoot(to, who); } + void RequestUCSServerStatus(); + void SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp); + bool IsUCSServerAvailable() { return m_ucss_available; } + // random object that provides random values for the zone EQEmu::Random random; @@ -355,6 +359,9 @@ private: Timer hotzone_timer; GlobalLootManager m_global_loot; + + bool m_ucss_available; + uint32 m_last_ucss_update; }; #endif diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index d5db09a53..cef24a649 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1623,6 +1623,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla if (account_id <= 0) return false; + std::string mail_key = database.GetMailKey(character_id); + clock_t t = std::clock(); /* Function timer start */ std::string query = StringFormat( "REPLACE INTO `character_data` (" @@ -1719,7 +1721,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla " e_aa_effects, " " e_percent_to_aa, " " e_expended_aa_spent, " - " e_last_invsnapshot " + " e_last_invsnapshot, " + " mailkey " ") " "VALUES (" "%u," // id " id, " @@ -1815,7 +1818,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla "%u," // e_aa_effects "%u," // e_percent_to_aa "%u," // e_expended_aa_spent - "%u" // e_last_invsnapshot + "%u," // e_last_invsnapshot + "'%s'" // mailkey mail_key ")", character_id, // " id, " account_id, // " account_id, " @@ -1910,7 +1914,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla m_epp->aa_effects, m_epp->perAA, m_epp->expended_aa, - m_epp->last_invsnapshot_time + m_epp->last_invsnapshot_time, + mail_key.c_str() ); auto results = database.QueryDatabase(query); Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); From b3ac1001c9fbcc711e3c56b7900b1945cb96f24c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 6 Mar 2018 23:01:25 -0500 Subject: [PATCH 42/59] Add a FixHeading function This is similar to what the client is doing Should be used when adding 2 headings together (or an EQ angle to a heading) --- common/misc_functions.cpp | 18 ++++++++++++++++++ common/misc_functions.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/common/misc_functions.cpp b/common/misc_functions.cpp index aaa20f3d4..e31da211f 100644 --- a/common/misc_functions.cpp +++ b/common/misc_functions.cpp @@ -242,6 +242,24 @@ int FloatToEQSpeedRun(float d) return static_cast(d * 40.0f); } +float FixHeading(float in) +{ + int i = 0; + if (in >= 512.0f) { + do { + in -= 512.0f; + } while (in >= 512.0f && i++ <= 5); + } + i = 0; + if (in < 0.0f) { + do { + in += 512.0f; + } while (in < 0.0f && i++ <= 5); + } + + return in; +} + /* Heading of 0 points in the pure positive Y direction diff --git a/common/misc_functions.h b/common/misc_functions.h index 9ecb08378..8214c7df0 100644 --- a/common/misc_functions.h +++ b/common/misc_functions.h @@ -66,6 +66,9 @@ int FloatToEQ10(float d); float EQSpeedRunToFloat(int d); int FloatToEQSpeedRun(float d); +// brings heading back into EQ angles range +float FixHeading(float in); + uint32 SwapBits21and22(uint32 mask); uint32 Catch22(uint32 mask); From 7e7f5f0bd67df3ad03d5baf7bc299441ea96a6c0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 6 Mar 2018 23:02:49 -0500 Subject: [PATCH 43/59] Add Map::FindClosestLoS() This will return true if LOS failed and you should use the outloc --- zone/map.cpp | 8 ++++++++ zone/map.h | 1 + 2 files changed, 9 insertions(+) diff --git a/zone/map.cpp b/zone/map.cpp index 32c6564b4..f198dc1de 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -268,6 +268,14 @@ bool Map::CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const { return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr); } +// returns true if outloc should be used +bool Map::FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) const { + if(!imp) + return false; + + return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, (RmReal *)&outloc, nullptr, nullptr); +} + inline bool file_exists(const std::string& name) { std::ifstream f(name.c_str()); return f.good(); diff --git a/zone/map.h b/zone/map.h index 35b5baeda..162985a36 100644 --- a/zone/map.h +++ b/zone/map.h @@ -42,6 +42,7 @@ public: bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const; bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; + bool FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) const; #ifdef USE_MAP_MMFS bool Load(std::string filename, bool force_mmf_overwrite = false); From 8f66527e02b1cb7b81ac5fc154958fde935a2896 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 6 Mar 2018 23:03:55 -0500 Subject: [PATCH 44/59] Add TryMoveAlong(distance, angle) This will take a distance you would like to move and an EQ angle you would like to move relative to current heading (so 0 is in front, 256 behind) Exported to Lua as well --- zone/lua_mob.cpp | 12 ++++++++++++ zone/lua_mob.h | 2 ++ zone/mob.h | 1 + zone/waypoints.cpp | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index bd5cddbcc..3793e6b9d 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -282,6 +282,16 @@ void Lua_Mob::GMMove(double x, double y, double z, double heading, bool send_upd self->GMMove(static_cast(x), static_cast(y), static_cast(z), static_cast(heading), send_update); } +void Lua_Mob::TryMoveAlong(float distance, float angle) { + Lua_Safe_Call_Void(); + self->TryMoveAlong(distance, angle); +} + +void Lua_Mob::TryMoveAlong(float distance, float angle, bool send) { + Lua_Safe_Call_Void(); + self->TryMoveAlong(distance, angle, send); +} + bool Lua_Mob::HasProcs() { Lua_Safe_Call_Bool(); return self->HasProcs(); @@ -2197,6 +2207,8 @@ luabind::scope lua_register_mob() { .def("GMMove", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::GMMove) .def("GMMove", (void(Lua_Mob::*)(double,double,double,double))&Lua_Mob::GMMove) .def("GMMove", (void(Lua_Mob::*)(double,double,double,double,bool))&Lua_Mob::GMMove) + .def("TryMoveAlong", (void(Lua_Mob::*)(float,float))&Lua_Mob::TryMoveAlong) + .def("TryMoveAlong", (void(Lua_Mob::*)(float,float,bool))&Lua_Mob::TryMoveAlong) .def("HasProcs", &Lua_Mob::HasProcs) .def("IsInvisible", (bool(Lua_Mob::*)(void))&Lua_Mob::IsInvisible) .def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 7aeb700e6..17dbc5c43 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -74,6 +74,8 @@ public: void GMMove(double x, double y, double z); void GMMove(double x, double y, double z, double heading); void GMMove(double x, double y, double z, double heading, bool send_update); + void TryMoveAlong(float distance, float heading); + void TryMoveAlong(float distance, float heading, bool send); bool HasProcs(); bool IsInvisible(); bool IsInvisible(Lua_Mob other); diff --git a/zone/mob.h b/zone/mob.h index 23c0004df..d19f4299c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -578,6 +578,7 @@ public: void SetFlyMode(uint8 flymode); inline void Teleport(glm::vec3 NewPosition) { m_Position.x = NewPosition.x; m_Position.y = NewPosition.y; m_Position.z = NewPosition.z; }; + void TryMoveAlong(float distance, float angle, bool send = true); //AI static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 45d199277..6cf74bb65 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -29,10 +29,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "npc.h" #include "quest_parser_collection.h" #include "water_map.h" +#include "fastmath.h" #include #include +extern FastMath g_Math; + struct wp_distance { float dist; @@ -990,6 +993,35 @@ float Mob::GetZOffset() const { return 0.2 * GetSize() * offset; } +// This function will try to move the mob along the relative angle a set distance +// if it can't be moved, it will lower the distance and try again +// If we want to move on like say a spawn, we can pass send as false +void Mob::TryMoveAlong(float distance, float angle, bool send) +{ + angle += GetHeading(); + angle = FixHeading(angle); + + glm::vec3 tmp_pos; + glm::vec3 new_pos = GetPosition(); + new_pos.x += distance * g_Math.FastSin(angle); + new_pos.y += distance * g_Math.FastCos(angle); + new_pos.z += GetZOffset(); + + if (zone->HasMap()) { + auto new_z = zone->zonemap->FindClosestZ(new_pos, nullptr); + if (new_z != BEST_Z_INVALID) + new_pos.z = new_z; + + if (zone->zonemap->FindClosestLoS(GetPosition(), new_pos, tmp_pos)) + new_pos = tmp_pos; + } + + new_pos.z = GetFixedZ(new_pos); + Teleport(new_pos); + if (send) + SendPositionUpdate(); +} + int ZoneDatabase::GetHighestGrid(uint32 zoneid) { std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid); From 0d63c8b9ef0dacbf82bbd28ea2fb73cf8138d0ad Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 7 Mar 2018 15:23:03 -0500 Subject: [PATCH 45/59] Trap auras should spawn a bit in front --- zone/aura.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/aura.cpp b/zone/aura.cpp index 08600811a..8a885c67b 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -758,6 +758,8 @@ void Mob::MakeAura(uint16 spell_id) auto npc = new Aura(npc_type, this, record); npc->SetAuraID(spell_id); + if (trap) + npc->TryMoveAlong(5.0f, 0.0f, false); // try to place 5 units in front entity_list.AddNPC(npc, false); if (trap) From ae966e546b93b42193147cf87cf1f9d67e4877a6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 7 Mar 2018 16:50:24 -0500 Subject: [PATCH 46/59] WIP for NPC spell push, off by default for now --- common/ruletypes.h | 1 + zone/attack.cpp | 22 +++------------- zone/map.cpp | 8 ++++++ zone/map.h | 1 + zone/mob.cpp | 1 + zone/mob.h | 6 +++++ zone/mob_ai.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++ zone/spells.cpp | 7 +++++ 8 files changed, 94 insertions(+), 18 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d80996184..6484a8586 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -402,6 +402,7 @@ RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spr RULE_BOOL(Spells, AllowItemTGB, false) // TGB doesn't work with items on live, custom servers want it though RULE_BOOL(Spells, NPCInnateProcOverride, true) // NPC innate procs override the target type to single target. RULE_BOOL(Spells, OldRainTargets, false) // use old incorrectly implemented max targets for rains +RULE_BOOL(Spells, NPCSpellPush, false) // enable spell push on NPCs RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/attack.cpp b/zone/attack.cpp index 90a774550..26121134e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3622,24 +3622,10 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const a->force = 0.0f; // 2013 change that disabled NPC vs NPC push else a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC - } - // update NPC stuff - if (a->force != 0.0f) { - auto new_pos = glm::vec3( - m_Position.x + (a->force * g_Math.FastSin(a->hit_heading) + m_Delta.x), - m_Position.y + (a->force * g_Math.FastCos(a->hit_heading) + m_Delta.y), m_Position.z); - if ((!IsNPC() || position_update_melee_push_timer.Check()) && zone->zonemap && - zone->zonemap->CheckLoS( - glm::vec3(m_Position), - new_pos)) { // If we have LoS on the new loc it should be reachable. - if (IsNPC()) { - // Is this adequate? - - Teleport(new_pos); - SendPositionUpdate(); - } - } else { - a->force = 0.0f; // we couldn't move there, so lets not + if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) { + m_Delta.x += a->force * g_Math.FastSin(a->hit_heading); + m_Delta.y += a->force * g_Math.FastCos(a->hit_heading); + ForcedMovement = 3; } } } diff --git a/zone/map.cpp b/zone/map.cpp index f198dc1de..487a9de0b 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -276,6 +276,14 @@ bool Map::FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) con return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, (RmReal *)&outloc, nullptr, nullptr); } +// returns true if a collision happens +bool Map::DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const { + if(!imp) + return false; + + return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, (RmReal *)&outnorm, (RmReal *)&distance); +} + inline bool file_exists(const std::string& name) { std::ifstream f(name.c_str()); return f.good(); diff --git a/zone/map.h b/zone/map.h index 162985a36..34638eec1 100644 --- a/zone/map.h +++ b/zone/map.h @@ -43,6 +43,7 @@ public: bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; bool FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) const; + bool DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const; #ifdef USE_MAP_MMFS bool Load(std::string filename, bool force_mmf_overwrite = false); diff --git a/zone/mob.cpp b/zone/mob.cpp index 6c3808549..d23aaf0b4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -393,6 +393,7 @@ Mob::Mob(const char* in_name, permarooted = (runspeed > 0) ? false : true; movetimercompleted = false; + ForcedMovement = 0; roamer = false; rooted = false; charmed = false; diff --git a/zone/mob.h b/zone/mob.h index d19f4299c..5530e0cab 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -50,6 +50,8 @@ struct AuraRecord; struct NewSpawn_Struct; struct PlayerPositionUpdateServer_Struct; +const int COLLISION_BOX_SIZE = 4; + namespace EQEmu { struct ItemData; @@ -1314,6 +1316,9 @@ protected: char lastname[64]; glm::vec4 m_Delta; + // just locs around them to double check, if we do expand collision this should be cached on movement + // ideally we should use real models, but this should be quick and work mostly + glm::vec4 m_CollisionBox[COLLISION_BOX_SIZE]; EQEmu::LightSourceProfile m_Light; @@ -1424,6 +1429,7 @@ protected: std::unique_ptr AI_movement_timer; std::unique_ptr AI_target_check_timer; bool movetimercompleted; + int8 ForcedMovement; // push bool permarooted; std::unique_ptr AI_scan_area_timer; std::unique_ptr AI_walking_timer; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 4d213dc7d..0b77cb87e 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -29,12 +29,16 @@ #include "quest_parser_collection.h" #include "string_ids.h" #include "water_map.h" +#include "fastmath.h" +#include #include #include +#include #include extern EntityList entity_list; +extern FastMath g_Math; extern Zone *zone; @@ -953,6 +957,68 @@ void Mob::AI_Process() { if (!(AI_think_timer->Check() || attack_timer.Check(false))) return; + // we are being pushed, we will hijack this movement timer + // this also needs to be done before casting to have a chance to interrupt + // this flag won't be set if the mob can't be pushed (rooted etc) + if (ForcedMovement && AI_movement_timer->Check()) { + bool bPassed = true; + auto z_off = GetZOffset(); + glm::vec3 normal; + glm::vec3 new_pos = m_Position + m_Delta; + new_pos.z += z_off; + + // no zone map = fucked + if (zone->HasMap()) { + // in front + m_CollisionBox[0].x = m_Position.x + 3.0f * g_Math.FastSin(0.0f); + m_CollisionBox[0].y = m_Position.y + 3.0f * g_Math.FastCos(0.0f); + m_CollisionBox[0].z = m_Position.z + z_off; + + // to right + m_CollisionBox[1].x = m_Position.x + 3.0f * g_Math.FastSin(128.0f); + m_CollisionBox[1].y = m_Position.y + 3.0f * g_Math.FastCos(128.0f); + m_CollisionBox[1].z = m_Position.z + z_off; + + // behind + m_CollisionBox[2].x = m_Position.x + 3.0f * g_Math.FastSin(256.0f); + m_CollisionBox[2].y = m_Position.y + 3.0f * g_Math.FastCos(256.0f); + m_CollisionBox[2].z = m_Position.z + z_off; + + // to left + m_CollisionBox[3].x = m_Position.x + 3.0f * g_Math.FastSin(384.0f); + m_CollisionBox[3].y = m_Position.y + 3.0f * g_Math.FastCos(384.0f); + m_CollisionBox[3].z = m_Position.z + z_off; + + // collision happened, need to move along the wall + float distance = 0.0f, shortest = std::numeric_limits::infinity(); + glm::vec3 tmp_nrm; + for (auto &vec : m_CollisionBox) { + if (zone->zonemap->DoCollisionCheck(vec, new_pos, tmp_nrm, distance)) { + bPassed = false; // lets try with new projection next pass + if (distance < shortest) { + normal = tmp_nrm; + shortest = distance; + } + } + } + } + + if (bPassed) { + ForcedMovement = 0; + m_Delta = glm::vec4(); + Teleport(new_pos); + SendPositionUpdate(); + pLastChange = Timer::GetCurrentTime(); + } else if (--ForcedMovement) { + auto proj = glm::proj(static_cast(m_Delta), normal); + m_Delta.x -= proj.x; + m_Delta.y -= proj.y; + m_Delta.z -= proj.z; + } else { + m_Delta = glm::vec4(); // well, we failed to find a spot to be forced to, lets give up + } + } + if (IsCasting()) return; diff --git a/zone/spells.cpp b/zone/spells.cpp index d5da2a34f..1c17e80f4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -81,6 +81,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "quest_parser_collection.h" #include "string_ids.h" #include "worldserver.h" +#include "fastmath.h" #include #include @@ -104,6 +105,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) extern Zone* zone; extern volatile bool is_zone_loaded; extern WorldServer worldserver; +extern FastMath g_Math; using EQEmu::CastingSlot; @@ -3945,6 +3947,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r { spelltar->CastToClient()->SetKnockBackExemption(true); } + } else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) { + spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading); + spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading); + spelltar->m_Delta.z += action->hit_pitch; + spelltar->ForcedMovement = 6; } } From 2da70c69da99848237e8debe6765bbd59f4ff075 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 7 Mar 2018 22:45:05 -0500 Subject: [PATCH 47/59] Added command '#ucs' to force re-connect after ucs server unavailability (must manually re-join channels for now) --- changelog.txt | 5 ++ common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + zone/command.cpp | 85 ++++++++++++++++++++++++++++++++ zone/command.h | 1 + 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 360f61082..f2f200fa6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/07/2018 == +Uleat: Added command '#ucs' to force a reconnect to UCS server. + - Works in place of client auto-reconnect packet in zones where feature is unsupported + - Currently, you will need to manually re-join channels + == 03/04/2018 == Uleat: Updated UCS versioning - SoF and higher clients have a new opcode identified (update your *.conf files) diff --git a/common/version.h b/common/version.h index d1afd9744..e003dc33f 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9121 +#define CURRENT_BINARY_DATABASE_VERSION 9122 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9018 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 2a0b12fd2..db0a04ca8 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -375,6 +375,7 @@ 9119|2018_02_10_GlobalLoot.sql|SHOW TABLES LIKE 'global_loot'|empty| 9120|2018_02_13_Heading.sql|SELECT value FROM variables WHERE varname = 'fixed_heading'|empty| 9121|2018_02_18_bug_reports.sql|SHOW TABLES LIKE 'bug_reports'|empty| +9122|2018_03_07_ucs_command.sql|SELECT * FROM `command_settings` WHERE `command` LIKE 'ucs'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/zone/command.cpp b/zone/command.cpp index 1fee0e26d..a97e04d96 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -389,6 +389,7 @@ int command_init(void) command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) || command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) || + command_add("ucs", "- Attempts to reconnect to the UCS server", 0, command_ucs) || command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || command_add("unlock", "- Unlock the worldserver", 150, command_unlock) || @@ -7158,6 +7159,90 @@ void command_undye(Client *c, const Seperator *sep) } } +void command_ucs(Client *c, const Seperator *sep) +{ + if (!c) + return; + + Log(Logs::Detail, Logs::UCS_Server, "Character %s attempting ucs reconnect while ucs server is %savailable", + c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); + + if (zone->IsUCSServerAvailable()) { + EQApplicationPacket* outapp = nullptr; + std::string buffer; + + std::string MailKey = database.GetMailKey(c->CharacterID(), true); + EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown; + + // chat server packet + switch (c->ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumChat; + break; + case EQEmu::versions::ClientVersion::SoF: + ConnectionType = EQEmu::versions::ucsSoFCombined; + break; + case EQEmu::versions::ClientVersion::SoD: + ConnectionType = EQEmu::versions::ucsSoDCombined; + break; + case EQEmu::versions::ClientVersion::UF: + ConnectionType = EQEmu::versions::ucsUFCombined; + break; + case EQEmu::versions::ClientVersion::RoF: + ConnectionType = EQEmu::versions::ucsRoFCombined; + break; + case EQEmu::versions::ClientVersion::RoF2: + ConnectionType = EQEmu::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQEmu::versions::ucsUnknown; + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + switch (c->ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + } +} + void command_undyeme(Client *c, const Seperator *sep) { if(c) { diff --git a/zone/command.h b/zone/command.h index b8d688a54..580dd00ff 100644 --- a/zone/command.h +++ b/zone/command.h @@ -302,6 +302,7 @@ void command_titlesuffix(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep); void command_trapinfo(Client* c, const Seperator *sep); void command_tune(Client *c, const Seperator *sep); +void command_ucs(Client *c, const Seperator *sep); void command_undye(Client *c, const Seperator *sep); void command_undyeme(Client *c, const Seperator *sep); void command_unfreeze(Client *c, const Seperator *sep); From 736890119b31363dc734eff2a0c62f28c37525d4 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 7 Mar 2018 22:46:11 -0500 Subject: [PATCH 48/59] Forgot sql script file... --- utils/sql/git/required/2018_03_07_ucs_command.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 utils/sql/git/required/2018_03_07_ucs_command.sql diff --git a/utils/sql/git/required/2018_03_07_ucs_command.sql b/utils/sql/git/required/2018_03_07_ucs_command.sql new file mode 100644 index 000000000..fdad0dda1 --- /dev/null +++ b/utils/sql/git/required/2018_03_07_ucs_command.sql @@ -0,0 +1 @@ +INSERT INTO `command_settings` VALUES ('ucs', '0', ''); From 49089f75374f552bf6c860b567810b96ee403a91 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 8 Mar 2018 12:57:40 -0500 Subject: [PATCH 49/59] Revert changes to melee push until the generic push processing is better --- zone/attack.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 26121134e..90a774550 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3622,10 +3622,24 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const a->force = 0.0f; // 2013 change that disabled NPC vs NPC push else a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC - if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) { - m_Delta.x += a->force * g_Math.FastSin(a->hit_heading); - m_Delta.y += a->force * g_Math.FastCos(a->hit_heading); - ForcedMovement = 3; + } + // update NPC stuff + if (a->force != 0.0f) { + auto new_pos = glm::vec3( + m_Position.x + (a->force * g_Math.FastSin(a->hit_heading) + m_Delta.x), + m_Position.y + (a->force * g_Math.FastCos(a->hit_heading) + m_Delta.y), m_Position.z); + if ((!IsNPC() || position_update_melee_push_timer.Check()) && zone->zonemap && + zone->zonemap->CheckLoS( + glm::vec3(m_Position), + new_pos)) { // If we have LoS on the new loc it should be reachable. + if (IsNPC()) { + // Is this adequate? + + Teleport(new_pos); + SendPositionUpdate(); + } + } else { + a->force = 0.0f; // we couldn't move there, so lets not } } } From ef487112df7baae7d332532a122c505ad721fc73 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 8 Mar 2018 16:11:48 -0500 Subject: [PATCH 50/59] Add FixZ() to the spell push stuff This is done after the packet since we want the NPC to get pushed up still. The client will still interp the fall but server side we'll be on the ground instantly --- zone/mob_ai.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 0b77cb87e..055200d56 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1009,6 +1009,7 @@ void Mob::AI_Process() { Teleport(new_pos); SendPositionUpdate(); pLastChange = Timer::GetCurrentTime(); + FixZ(); // so we teleport to the ground locally, we want the client to interpolate falling etc } else if (--ForcedMovement) { auto proj = glm::proj(static_cast(m_Delta), normal); m_Delta.x -= proj.x; From 5c87b8152db107896d5c84533b5727ec1de54530 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 8 Mar 2018 18:03:15 -0500 Subject: [PATCH 51/59] Remove Map::FindClosestLoS() Map::LineIntersectsZone() already accomplished this use case --- zone/map.cpp | 8 -------- zone/map.h | 1 - zone/waypoints.cpp | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/zone/map.cpp b/zone/map.cpp index 487a9de0b..0627c2dc2 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -268,14 +268,6 @@ bool Map::CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const { return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr); } -// returns true if outloc should be used -bool Map::FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) const { - if(!imp) - return false; - - return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, (RmReal *)&outloc, nullptr, nullptr); -} - // returns true if a collision happens bool Map::DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const { if(!imp) diff --git a/zone/map.h b/zone/map.h index 34638eec1..8a2ec7f68 100644 --- a/zone/map.h +++ b/zone/map.h @@ -42,7 +42,6 @@ public: bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const; bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; - bool FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) const; bool DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const; #ifdef USE_MAP_MMFS diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 6cf74bb65..55a2cc511 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -1012,7 +1012,7 @@ void Mob::TryMoveAlong(float distance, float angle, bool send) if (new_z != BEST_Z_INVALID) new_pos.z = new_z; - if (zone->zonemap->FindClosestLoS(GetPosition(), new_pos, tmp_pos)) + if (zone->zonemap->LineIntersectsZone(GetPosition(), new_pos, 0.0f, &tmp_pos)) new_pos = tmp_pos; } From 876335bb54a5ef0c9f7954a0c4b1d1bc51e27e5c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 8 Mar 2018 19:42:40 -0500 Subject: [PATCH 52/59] Pull out forced movement from push to it's own function Had to pull this out so we could process it before mez/stun --- zone/mob.h | 1 + zone/mob_ai.cpp | 20 ++++++++++++-------- zone/npc.cpp | 4 ++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 5530e0cab..a160b2a45 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -581,6 +581,7 @@ public: inline void Teleport(glm::vec3 NewPosition) { m_Position.x = NewPosition.x; m_Position.y = NewPosition.y; m_Position.z = NewPosition.z; }; void TryMoveAlong(float distance, float angle, bool send = true); + void ProcessForcedMovement(); //AI static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 055200d56..70176a92e 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -950,17 +950,12 @@ void Client::AI_Process() } } -void Mob::AI_Process() { - if (!IsAIControlled()) - return; - - if (!(AI_think_timer->Check() || attack_timer.Check(false))) - return; - +void Mob::ProcessForcedMovement() +{ // we are being pushed, we will hijack this movement timer // this also needs to be done before casting to have a chance to interrupt // this flag won't be set if the mob can't be pushed (rooted etc) - if (ForcedMovement && AI_movement_timer->Check()) { + if (AI_movement_timer->Check()) { bool bPassed = true; auto z_off = GetZOffset(); glm::vec3 normal; @@ -1019,6 +1014,15 @@ void Mob::AI_Process() { m_Delta = glm::vec4(); // well, we failed to find a spot to be forced to, lets give up } } +} + +void Mob::AI_Process() { + if (!IsAIControlled()) + return; + + if (!(AI_think_timer->Check() || attack_timer.Check(false))) + return; + if (IsCasting()) return; diff --git a/zone/npc.cpp b/zone/npc.cpp index 0fcc1e17d..c872fb680 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -762,6 +762,10 @@ bool NPC::Process() reface_timer->Disable(); } + // needs to be done before mez and stun + if (ForcedMovement) + ProcessForcedMovement(); + if (IsMezzed()) return true; From d59931dc4db8879f55fbf2760c59b17941260c0a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 9 Mar 2018 17:10:34 -0500 Subject: [PATCH 53/59] Use an algorithm to generate pet names --- common/spdat.h | 1 - zone/pets.cpp | 105 ++++++++++++++----------------------------------- 2 files changed, 29 insertions(+), 77 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 51ec8327b..c6aab7e7e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -972,7 +972,6 @@ bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); int CalcPetHp(int levelb, int classb, int STA = 75); -const char *GetRandPetName(); int GetSpellEffectDescNum(uint16 spell_id); DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType = 0); bool DetrimentalSpellAllowsRest(uint16 spell_id); diff --git a/zone/pets.cpp b/zone/pets.cpp index bbc4f8eea..a602153b2 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -28,6 +28,8 @@ #include "pets.h" #include "zonedb.h" +#include + #ifdef BOTS #include "bot.h" #endif @@ -38,82 +40,33 @@ #endif -const char *GetRandPetName() +// need to pass in a char array of 64 chars +void GetRandPetName(char *name) { - static const char *petnames[] = { "Gabaner","Gabann","Gabantik","Gabarab","Gabarer","Gabarn","Gabartik", - "Gabekab","Gabeker","Gabekn","Gaber","Gabn","Gabobab","Gabobn","Gabtik", - "Ganer","Gann","Gantik","Garab","Garaner","Garann","Garantik","Gararn", - "Garekn","Garer","Garn","Gartik","Gasaner","Gasann","Gasantik","Gasarer", - "Gasartik","Gasekn","Gaser","Gebann","Gebantik","Gebarer","Gebarn","Gebartik", - "Gebeker","Gebekn","Gebn","Gekab","Geker","Gekn","Genaner","Genann","Genantik", - "Genarer","Genarn","Gener","Genn","Genobtik","Gibaner","Gibann","Gibantik", - "Gibarn","Gibartik","Gibekn","Giber","Gibn","Gibobtik","Gibtik","Gobaber", - "Gobaner","Gobann","Gobarn","Gobartik","Gober","Gobn","Gobober","Gobobn", - "Gobobtik","Gobtik","Gonaner","Gonann","Gonantik","Gonarab","Gonarer", - "Gonarn","Gonartik","Gonekab","Gonekn","Goner","Gonobtik","Gontik","Gotik", - "Jabaner","Jabann","Jabantik","Jabarab","Jabarer","Jabarn","Jabartik", - "Jabekab","Jabeker","Jabekn","Jaber","Jabn","Jabobtik","Jabtik","Janab", - "Janer","Jann","Jantik","Jarab","Jaranab","Jaraner","Jararer","Jararn", - "Jarartik","Jareker","Jarekn","Jarer","Jarn","Jarobn","Jarobtik","Jartik", - "Jasab","Jasaner","Jasantik","Jasarer","Jasartik","Jasekab","Jaseker", - "Jasekn","Jaser","Jasn","Jasobab","Jasober","Jastik","Jebanab","Jebann", - "Jebantik","Jebarab","Jebarar","Jebarer","Jebarn","Jebartik","Jebeker", - "Jebekn","Jeber","Jebobn","Jebtik","Jekab","Jeker","Jekn","Jenann", - "Jenantik","Jenarer","Jeneker","Jenekn","Jentik","Jibaner","Jibann", - "Jibantik","Jibarer","Jibarn","Jibartik","Jibeker","Jibn","Jibobn", - "Jibtik","Jobab","Jobaner","Jobann","Jobantik","Jobarn","Jobartik", - "Jobekab","Jobeker","Jober","Jobn","Jobtik","Jonanab","Jonaner", - "Jonann","Jonantik","Jonarer","Jonarn","Jonartik","Jonekab","Joneker", - "Jonekn","Joner","Jonn","Jonnarn","Jonober","Jonobn","Jonobtik","Jontik", - "Kabanab","Kabaner","Kabann","Kabantik","Kabarer","Kabarn","Kabartik", - "Kabeker","Kabekn","Kaber","Kabn","Kabober","Kabobn","Kabobtik","Kabtik", - "Kanab","Kaner","Kann","Kantik","Karab","Karanab","Karaner","Karann", - "Karantik","Kararer","Karartik","Kareker","Karer","Karn","Karobab","Karobn", - "Kartik","Kasaner","Kasann","Kasarer","Kasartik","Kaseker","Kasekn","Kaser", - "Kasn","Kasober","Kastik","Kebann","Kebantik","Kebarab","Kebartik","Kebeker", - "Kebekn","Kebn","Kebobab","Kebtik","Kekab","Keker","Kekn","Kenab","Kenaner", - "Kenantik","Kenarer","Kenarn","Keneker","Kener","Kenn","Kenobn","Kenobtik", - "Kentik","Kibab","Kibaner","Kibantik","Kibarn","Kibartik","Kibekab","Kibeker", - "Kibekn","Kibn","Kibobn","Kibobtik","Kobab","Kobanab","Kobaner","Kobann", - "Kobantik","Kobarer","Kobarn","Kobartik","Kobeker","Kobekn","Kober","Kobn", - "Kobober","Kobobn","Kobtik","Konanab","Konaner","Konann","Konantik","Konarab", - "Konarer","Konarn","Konekab","Koneker","Konekn","Koner","Konn","Konobn", - "Konobtik","Kontik","Labanab","Labaner","Labann","Labarab","Labarer", - "Labarn","Labartik","Labeker","Labekn","Laner","Lann","Larab","Larantik", - "Lararer","Lararn","Larartik","Lareker","Larer","Larn","Lartik","Lasaner", - "Lasann","Lasarer","Laseker","Laser","Lasik","Lasn","Lastik","Lebaner", - "Lebarer","Lebartik","Lebekn","Lebtik","Lekab","Lekn","Lenanab","Lenaner", - "Lenann","Lenartik","Lenekab","Leneker","Lenekn","Lentik","Libab","Libaner", - "Libann","Libantik","Libarer","Libarn","Libartik","Libeker","Libekn","Lobann", - "Lobarab","Lobarn","Lobartik","Lobekn","Lobn","Lobober","Lobobn","Lobtik", - "Lonaner","Lonann","Lonantik","Lonarab","Lonarer","Lonarn","Lonartik","Lonekn", - "Loner","Lonobtik","Lontik","Vabanab","Vabaner","Vabann","Vabantik","Vabarer", - "Vabarn","Vabartik","Vabeker","Vabekn","Vabtik","Vanikk","Vann","Varartik","Varn", - "Vartik","Vasann","Vasantik","Vasarab","Vasarer","Vaseker","Vebaner","Vebantik", - "Vebarab","Vebeker","Vebekn","Vebobn","Vekab","Veker","Venaner","Venantik","Venar", - "Venarn","Vener","Ventik","Vibann","Vibantik","Viber","Vibobtik","Vobann", - "Vobarer","Vobartik","Vobekn","Vober","Vobn","Vobtik","Vonaner","Vonann", - "Vonantik","Vonarab","Vonarn","Vonartik","Voneker","Vonn","Xabanab","Xabaner", - "Xabarer","Xabarn","Xabartik","Xabekab","Xabeker","Xabekn","Xaber","Xabober", - "Xaner","Xann","Xarab","Xaranab","Xarann","Xarantik","Xararer","Xarartik","Xarer", - "Xarn","Xartik","Xasaner","Xasann","Xasarab","Xasarn","Xasekab","Xaseker", - "Xebarer","Xebarn","Xebeker","Xeber","Xebober","Xebtik","Xekab","Xeker", - "Xekn","Xenann","Xenantik","Xenarer","Xenartik","Xenekn","Xener","Xenober", - "Xentik","Xibantik","Xibarer","Xibekab","Xibeker","Xibobab","Xibober","Xibobn", - "Xobaner","Xobann","Xobarab","Xobarn","Xobekab","Xobeker","Xobekn","Xober", - "Xobn","Xobobn","Xobtik","Xonaner","Xonann","Xonantik","Xonarer","Xonartik", - "Xonekab","Xoneker","Xonekn","Xoner","Xonober","Xtik","Zabaner","Zabantik", - "Zabarab","Zabekab","Zabekn","Zaber","Zabn","Zabobab","Zabober","Zabtik", - "Zaner","Zantik","Zarann","Zarantik","Zararn","Zarartik","Zareker","Zarekn", - "Zarer","Zarn","Zarober","Zartik","Zasaner","Zasarer","Zaseker","Zasekn","Zasn", - "Zebantik","Zebarer","Zebarn","Zebartik","Zebobab","Zekab","Zekn","Zenann", - "Zenantik","Zenarer","Zenarn","Zenekab","Zeneker","Zenobtik","Zibanab","Zibaner", - "Zibann","Zibarer","Zibartik","Zibekn","Zibn","Zibobn","Zobaner","Zobann", - "Zobarn","Zober","Zobn","Zonanab","Zonaner","Zonann","Zonantik","Zonarer", - "Zonartik","Zonobn","Zonobtik","Zontik","Ztik" }; - int r = zone->random.Int(0, (sizeof(petnames)/sizeof(const char *))-1); - printf("Pet being created: %s\n",petnames[r]); // DO NOT COMMENT THIS OUT! - return petnames[r]; + std::string temp; + temp.reserve(64); + static const char *part1[] = {"G", "J", "K", "L", "V", "X", "Z"}; + static const char *part2[] = {nullptr, "ab", "ar", "as", "eb", "en", "ib", "ob", "on"}; + static const char *part3[] = {nullptr, "an", "ar", "ek", "ob"}; + static const char *part4[] = {"ab", "er", "n", "tik"}; + + const char *first = part1[zone->random.Int(0, (sizeof(part1) / sizeof(const char *)) - 1)]; + const char *second = part2[zone->random.Int(0, (sizeof(part2) / sizeof(const char *)) - 1)]; + const char *third = part3[zone->random.Int(0, (sizeof(part3) / sizeof(const char *)) - 1)]; + const char *fourth = part4[zone->random.Int(0, (sizeof(part4) / sizeof(const char *)) - 1)]; + + // if both of these are empty, we would get an illegally short name + if (second == nullptr && third == nullptr) + fourth = part4[(sizeof(part4) / sizeof(const char *)) - 1]; + + temp += first; + if (second != nullptr) + temp += second; + if (third != nullptr) + temp += third; + temp += fourth; + + strn0cpy(name, temp.c_str(), 64); } //not used anymore @@ -325,7 +278,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, } else if (record.petnaming == 4) { // Keep the DB name } else if (record.petnaming == 3 && IsClient()) { - strcpy(npc_type->name, GetRandPetName()); + GetRandPetName(npc_type->name); } else if (record.petnaming == 5 && IsClient()) { strcpy(npc_type->name, this->GetName()); npc_type->name[24] = '\0'; From ca29cb037e2a75854ffc08eb8cd6099e14782997 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 9 Mar 2018 17:26:22 -0500 Subject: [PATCH 54/59] Add some more name filtering --- zone/pets.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/pets.cpp b/zone/pets.cpp index a602153b2..be17a3293 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -45,10 +45,11 @@ void GetRandPetName(char *name) { std::string temp; temp.reserve(64); + // note these orders are used to make the exclusions cheap :P static const char *part1[] = {"G", "J", "K", "L", "V", "X", "Z"}; static const char *part2[] = {nullptr, "ab", "ar", "as", "eb", "en", "ib", "ob", "on"}; static const char *part3[] = {nullptr, "an", "ar", "ek", "ob"}; - static const char *part4[] = {"ab", "er", "n", "tik"}; + static const char *part4[] = {"er", "ab", "n", "tik"}; const char *first = part1[zone->random.Int(0, (sizeof(part1) / sizeof(const char *)) - 1)]; const char *second = part2[zone->random.Int(0, (sizeof(part2) / sizeof(const char *)) - 1)]; @@ -59,6 +60,14 @@ void GetRandPetName(char *name) if (second == nullptr && third == nullptr) fourth = part4[(sizeof(part4) / sizeof(const char *)) - 1]; + // "ektik" isn't allowed either I guess? + if (third == part3[3] && fourth == part4[3]) + fourth = part4[zone->random.Int(0, (sizeof(part4) / sizeof(const char *)) - 2)]; + + // "Laser" isn't allowed either I guess? + if (first == part1[3] && second == part2[3] && third == nullptr && fourth == part4[0]) + fourth = part4[zone->random.Int(1, (sizeof(part4) / sizeof(const char *)) - 2)]; + temp += first; if (second != nullptr) temp += second; From a6b31017e34309a7a9328d4468f64a70247ad69e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 9 Mar 2018 19:27:31 -0500 Subject: [PATCH 55/59] Remove extra z offset --- zone/mob_ai.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 70176a92e..18fc5d441 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -960,7 +960,6 @@ void Mob::ProcessForcedMovement() auto z_off = GetZOffset(); glm::vec3 normal; glm::vec3 new_pos = m_Position + m_Delta; - new_pos.z += z_off; // no zone map = fucked if (zone->HasMap()) { From 341bc0723bd917dffd5d26b7c5761fab70ae364f Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 9 Mar 2018 23:25:50 -0500 Subject: [PATCH 56/59] Test code for tracing zone crashes --- zone/client_packet.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fe8af2a98..dc198056b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6728,7 +6728,11 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) // Inviter and Invitee are in the same zone if (inviter != nullptr && inviter->IsClient()) { - if (GroupFollow(inviter->CastToClient())) + if (!inviter->CastToClient()->Connected()) + { + Log(Logs::General, Logs::Error, "%s attempted to join group while leader %s was zoning.", GetName(), inviter->GetName()); + } + else if (GroupFollow(inviter->CastToClient())) { strn0cpy(gf->name1, inviter->GetName(), sizeof(gf->name1)); strn0cpy(gf->name2, GetName(), sizeof(gf->name2)); From 89303d9218fdf0ef2ce6ba8a573cd63d7926f286 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 9 Mar 2018 23:41:27 -0500 Subject: [PATCH 57/59] Fix for zone crashes on startup --- zone/worldserver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 053bebacf..7c60bffe0 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1817,7 +1817,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_UCSServerStatusReply: { auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer; - zone->SetUCSServerAvailable((ucsss->available != 0), ucsss->timestamp); + if (zone) + zone->SetUCSServerAvailable((ucsss->available != 0), ucsss->timestamp); break; } case ServerOP_CZSetEntityVariableByNPCTypeID: From fdef1090c3485a4f6e40e43dc6afa9fee72339d6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 10 Mar 2018 15:11:16 -0500 Subject: [PATCH 58/59] Update #race to accept all valid RoF2 races --- zone/command.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index a97e04d96..4ca94fcb4 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2323,14 +2323,18 @@ void command_race(Client *c, const Seperator *sep) { Mob *t=c->CastToMob(); - // Need to figure out max race for LoY/LDoN: going with upper bound of 500 now for testing - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 724) { - if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) - t=c->GetTarget(); - t->SendIllusionPacket(atoi(sep->arg[1])); + if (sep->IsNumber(1)) { + auto race = atoi(sep->arg[1]); + if ((race >= 0 && race <= 732) || (race >= 2253 && race <= 2259)) { + if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) + t = c->GetTarget(); + t->SendIllusionPacket(race); + } else { + c->Message(0, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); + } + } else { + c->Message(0, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); } - else - c->Message(0, "Usage: #race [0-724] (0 for back to normal)"); } void command_gender(Client *c, const Seperator *sep) From 638d43e3cfdf3fd4fc04aadb901a7fb6ec04003c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 10 Mar 2018 15:36:12 -0500 Subject: [PATCH 59/59] Use pathpoint races for showing nodes The nearest neighbor command will grow them instead of changing race now. --- zone/pathing.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 0bc054d58..7f2d8c880 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -560,7 +560,8 @@ void PathManager::SpawnPathNodes() sprintf(npc_type->lastname, "%i", PathNodes[i].id); npc_type->cur_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 151; + npc_type->race = 2253; + npc_type->size = 3.0f; npc_type->gender = 2; npc_type->class_ = 9; npc_type->deity= 1; @@ -1377,7 +1378,7 @@ void PathManager::ShowPathNodeNeighbours(Client *c) Mob *m = entity_list.GetMob(Name); if(m) - m->SendIllusionPacket(151); + m->ChangeSize(3.0f); } std::stringstream Neighbours; @@ -1401,7 +1402,7 @@ void PathManager::ShowPathNodeNeighbours(Client *c) Mob *m = entity_list.GetMob(Name); if(m) - m->SendIllusionPacket(46); + m->ChangeSize(5.0f); } c->Message(0, "Neighbours: %s", Neighbours.str().c_str()); } @@ -1560,7 +1561,8 @@ int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 reques sprintf(npc_type->lastname, "%i", new_id); npc_type->cur_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 151; + npc_type->race = 2253; + npc_type->size = 3.0f; npc_type->gender = 2; npc_type->class_ = 9; npc_type->deity= 1; @@ -1621,7 +1623,8 @@ int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 reques sprintf(npc_type->lastname, "%i", new_id); npc_type->cur_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 151; + npc_type->race = 2253; + npc_type->size = 3.0f; npc_type->gender = 2; npc_type->class_ = 9; npc_type->deity= 1;