diff --git a/changelog.txt b/changelog.txt index 60af1336d..931ccf079 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/20/2015 == +demonstar55: Implement claims for RoF/RoF2 (should no longer crash the client!) + - fixed bugs related to claims for the rest of the clients (woo infinite loops) + == 02/18/2015 == Trevius: Fix for potential recursive loops if using RemoveFromHateList() within EVENT_HATE_LIST. diff --git a/common/base_packet.h b/common/base_packet.h index facd98809..027b8d2fb 100644 --- a/common/base_packet.h +++ b/common/base_packet.h @@ -63,6 +63,7 @@ public: void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); } void WriteDouble(double value) { *(double *)(pBuffer + _wpos) = value; _wpos += sizeof(double); } void WriteString(const char * str) { uint32 len = static_cast(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; } + void WriteData(const void *ptr, size_t n) { memcpy(pBuffer + _wpos, ptr, n); _wpos += n; } uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; } uint8 ReadUInt8(uint32 Offset) const { uint8 value = *(uint8 *)(pBuffer + Offset); return value; } diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 3f14e1cc9..27fe9bb2b 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4553,19 +4553,12 @@ struct InternalVeteranReward /*012*/ InternalVeteranRewardItem items[8]; }; -struct VeteranClaimReply +struct VeteranClaim { -/*000*/ char name[64]; -/*064*/ uint32 claim_id; -/*068*/ uint32 reject_field; -/*072*/ uint32 unknown072; -}; - -struct VeteranClaimRequest -{ -/*000*/ char name_data[64]; //name + other data +/*000*/ char name[64]; //name + other data /*064*/ uint32 claim_id; /*068*/ uint32 unknown068; +/*072*/ uint32 action; }; struct GMSearchCorpse_Struct diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 485dc4118..d6723db4d 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include namespace RoF { @@ -3608,37 +3610,71 @@ namespace RoF FINISH_ENCODE(); } + ENCODE(OP_VetClaimReply) + { + ENCODE_LENGTH_EXACT(VeteranClaim); + SETUP_DIRECT_ENCODE(VeteranClaim, structs::VeteranClaim); + + memcpy(eq->name, emu->name, sizeof(emu->name)); + OUT(claim_id); + OUT(action); + + FINISH_ENCODE(); + } + ENCODE(OP_VetRewardsAvaliable) { EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; + auto __emu_buffer = inapp->pBuffer; uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for (unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + // calculate size of names, note the packet DOES NOT have null termed c-strings + std::vector name_lengths; + for (int i = 0; i < count; ++i) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for (int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); - vr->items[x].charges = ivr->items[x].charges; + for (int i = 0; i < ivr->claim_count; i++) { + uint32 length = strnlen(ivr->items[i].item_name, 63); + if (length) + name_lengths.push_back(length); } - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + __emu_buffer += sizeof(InternalVeteranReward); } - dest->FastQueuePacket(&outapp_create); + uint32 packet_size = std::accumulate(name_lengths.begin(), name_lengths.end(), 0) + + sizeof(structs::VeteranReward) + (sizeof(structs::VeteranRewardEntry) * count) + + // size of name_lengths is the same as item count + (sizeof(structs::VeteranRewardItem) * name_lengths.size()); + + // build packet now! + auto outapp = new EQApplicationPacket(OP_VetRewardsAvaliable, packet_size); + __emu_buffer = inapp->pBuffer; + + outapp->WriteUInt32(count); + auto name_itr = name_lengths.begin(); + for (int i = 0; i < count; i++) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; + + outapp->WriteUInt32(ivr->claim_id); + outapp->WriteUInt32(ivr->number_available); + outapp->WriteUInt32(ivr->claim_count); + outapp->WriteUInt8(1); // enabled + + for (int j = 0; j < ivr->claim_count; j++) { + assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert + outapp->WriteUInt32(*name_itr); + outapp->WriteData(ivr->items[j].item_name, *name_itr); + outapp->WriteUInt32(ivr->items[j].item_id); + outapp->WriteUInt32(ivr->items[j].charges); + ++name_itr; + } + + __emu_buffer += sizeof(InternalVeteranReward); + } + + dest->FastQueuePacket(&outapp); delete inapp; } @@ -4924,6 +4960,16 @@ namespace RoF FINISH_DIRECT_DECODE(); } + DECODE(OP_VetClaimRequest) + { + DECODE_LENGTH_EXACT(structs::VeteranClaim); + SETUP_DIRECT_DECODE(VeteranClaim, structs::VeteranClaim); + + IN(claim_id); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ZoneChange) { DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index e1f3b071a..493f369e1 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include namespace RoF2 { @@ -3749,37 +3751,71 @@ namespace RoF2 FINISH_ENCODE(); } + ENCODE(OP_VetClaimReply) + { + ENCODE_LENGTH_EXACT(VeteranClaim); + SETUP_DIRECT_ENCODE(VeteranClaim, structs::VeteranClaim); + + memcpy(eq->name, emu->name, sizeof(emu->name)); + OUT(claim_id); + OUT(action); + + FINISH_ENCODE(); + } + ENCODE(OP_VetRewardsAvaliable) { EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; + auto __emu_buffer = inapp->pBuffer; uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for (unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + // calculate size of names, note the packet DOES NOT have null termed c-strings + std::vector name_lengths; + for (int i = 0; i < count; ++i) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for (int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); - vr->items[x].charges = ivr->items[x].charges; + for (int i = 0; i < ivr->claim_count; i++) { + uint32 length = strnlen(ivr->items[i].item_name, 63); + if (length) + name_lengths.push_back(length); } - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + __emu_buffer += sizeof(InternalVeteranReward); } - dest->FastQueuePacket(&outapp_create); + uint32 packet_size = std::accumulate(name_lengths.begin(), name_lengths.end(), 0) + + sizeof(structs::VeteranReward) + (sizeof(structs::VeteranRewardEntry) * count) + + // size of name_lengths is the same as item count + (sizeof(structs::VeteranRewardItem) * name_lengths.size()); + + // build packet now! + auto outapp = new EQApplicationPacket(OP_VetRewardsAvaliable, packet_size); + __emu_buffer = inapp->pBuffer; + + outapp->WriteUInt32(count); + auto name_itr = name_lengths.begin(); + for (int i = 0; i < count; i++) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; + + outapp->WriteUInt32(ivr->claim_id); + outapp->WriteUInt32(ivr->number_available); + outapp->WriteUInt32(ivr->claim_count); + outapp->WriteUInt8(1); // enabled + + for (int j = 0; j < ivr->claim_count; j++) { + assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert + outapp->WriteUInt32(*name_itr); + outapp->WriteData(ivr->items[j].item_name, *name_itr); + outapp->WriteUInt32(ivr->items[j].item_id); + outapp->WriteUInt32(ivr->items[j].charges); + ++name_itr; + } + + __emu_buffer += sizeof(InternalVeteranReward); + } + + dest->FastQueuePacket(&outapp); delete inapp; } @@ -5122,6 +5158,16 @@ namespace RoF2 FINISH_DIRECT_DECODE(); } + DECODE(OP_VetClaimRequest) + { + DECODE_LENGTH_EXACT(structs::VeteranClaim); + SETUP_DIRECT_DECODE(VeteranClaim, structs::VeteranClaim); + + IN(claim_id); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ZoneChange) { DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 3edcac459..4e4400cdf 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -111,6 +111,7 @@ E(OP_Trader) E(OP_TraderBuy) E(OP_TributeInfo) E(OP_TributeItem) +E(OP_VetClaimReply) E(OP_VetRewardsAvaliable) E(OP_WearChange) E(OP_WhoAllResponse) @@ -174,6 +175,7 @@ D(OP_Trader) D(OP_TraderBuy) D(OP_TradeSkillCombine) D(OP_TributeItem) +D(OP_VetClaimRequest) D(OP_WhoAllRequest) D(OP_ZoneChange) D(OP_ZoneEntry) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 520e9066e..0f2e4548b 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4727,17 +4727,33 @@ struct AugmentInfo_Struct struct VeteranRewardItem { -/*000*/ uint32 item_id; -/*004*/ uint32 charges; -/*008*/ char item_name[64]; +/*000*/ uint32 name_length; +/*004*/ char item_name[0]; // THIS IS NOT NULL TERMED +/*???*/ uint32 item_id; +/*???*/ uint32 charges; +}; + +struct VeteranRewardEntry +{ +/*000*/ uint32 claim_id; // guessed +/*004*/ uint32 avaliable_count; +/*008*/ uint32 claim_count; +/*012*/ char enabled; +/*013*/ VeteranRewardItem items[0]; }; struct VeteranReward { -/*000*/ uint32 claim_id; -/*004*/ uint32 number_available; -/*008*/ uint32 claim_count; -/*012*/ VeteranRewardItem items[8]; +/*000*/ uint32 claim_count; +/*004*/ VeteranRewardEntry entries[0]; +}; + +struct VeteranClaim +{ +/*000*/ char name[68]; //name + other data +/*068*/ uint32 claim_id; +/*072*/ uint32 unknown072; +/*076*/ uint32 action; }; struct ExpeditionEntryHeader_Struct diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 8c55cd857..08e8647f3 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -96,6 +96,7 @@ E(OP_Trader) E(OP_TraderBuy) E(OP_TributeInfo) E(OP_TributeItem) +E(OP_VetClaimReply) E(OP_VetRewardsAvaliable) E(OP_WearChange) E(OP_WhoAllResponse) @@ -159,6 +160,7 @@ D(OP_Trader) D(OP_TraderBuy) D(OP_TradeSkillCombine) D(OP_TributeItem) +D(OP_VetClaimRequest) D(OP_WhoAllRequest) D(OP_ZoneChange) D(OP_ZoneEntry) diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 4b8fe3b01..05943d81a 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4718,17 +4718,33 @@ struct AugmentInfo_Struct struct VeteranRewardItem { -/*000*/ uint32 item_id; -/*004*/ uint32 charges; -/*008*/ char item_name[64]; +/*000*/ uint32 name_length; +/*004*/ char item_name[0]; // THIS IS NOT NULL TERMED +/*???*/ uint32 item_id; +/*???*/ uint32 charges; +}; + +struct VeteranRewardEntry +{ +/*000*/ uint32 claim_id; // guessed +/*004*/ uint32 avaliable_count; +/*008*/ uint32 claim_count; +/*012*/ char enabled; +/*013*/ VeteranRewardItem items[0]; }; struct VeteranReward { -/*000*/ uint32 claim_id; -/*004*/ uint32 number_available; -/*008*/ uint32 claim_count; -/*012*/ VeteranRewardItem items[8]; +/*000*/ uint32 claim_count; +/*004*/ VeteranRewardEntry entries[0]; +}; + +struct VeteranClaim +{ +/*000*/ char name[68]; //name + other data +/*068*/ uint32 claim_id; +/*072*/ uint32 unknown072; +/*076*/ uint32 action; }; struct ExpeditionEntryHeader_Struct diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 841f77bf5..2660738de 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -525,7 +525,7 @@ OP_KnowledgeBase=0x0000 OP_SlashAdventure=0x571a # /adventure OP_VetRewardsAvaliable=0x0557 OP_VetClaimRequest=0x6ba0 -OP_VetClaimReply=0x0000 +OP_VetClaimReply=0x407e OP_BecomePVPPrompt=0x36B2 #guessed from ASM OP_PVPStats=0x5cc0 OP_PVPLeaderBoardRequest=0x61d2 diff --git a/zone/client.cpp b/zone/client.cpp index 227bb4083..e72672369 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5362,35 +5362,35 @@ void Client::SendRewards() FastQueuePacket(&vetapp); } -bool Client::TryReward(uint32 claim_id) { - //Make sure we have an open spot - //Make sure we have it in our acct and count > 0 - //Make sure the entry was found - //If we meet all the criteria: - //Decrement our count by 1 if it > 1 delete if it == 1 - //Create our item in bag if necessary at the free inv slot - //save +bool Client::TryReward(uint32 claim_id) +{ + // Make sure we have an open spot + // Make sure we have it in our acct and count > 0 + // Make sure the entry was found + // If we meet all the criteria: + // Decrement our count by 1 if it > 1 delete if it == 1 + // Create our item in bag if necessary at the free inv slot + // save uint32 free_slot = 0xFFFFFFFF; - for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { + for (int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { ItemInst *item = GetInv().GetItem(i); - if(!item) { + if (!item) { free_slot = i; break; } } - if(free_slot == 0xFFFFFFFF) + if (free_slot == 0xFFFFFFFF) return false; char errbuf[MYSQL_ERRMSG_SIZE]; std::string query = StringFormat("SELECT amount FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); - if (!results.Success()) { + if (!results.Success()) return false; - } if (results.RowCount() == 0) return false; @@ -5398,52 +5398,49 @@ bool Client::TryReward(uint32 claim_id) { auto row = results.begin(); uint32 amt = atoi(row[0]); - if(amt == 0) + if (amt == 0) return false; - std::list::iterator iter = zone->VeteranRewards.begin(); - for (; iter != zone->VeteranRewards.end(); ++row) - if((*iter).claim_id == claim_id) - break; + auto iter = std::find_if(zone->VeteranRewards.begin(), zone->VeteranRewards.end(), + [claim_id](const InternalVeteranReward &a) { return a.claim_id == claim_id; }); - if(iter == zone->VeteranRewards.end()) + if (iter == zone->VeteranRewards.end()) return false; - if(amt == 1) { + if (amt == 1) { query = StringFormat("DELETE FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); - } - else { + } else { query = StringFormat("UPDATE account_rewards SET amount = (amount-1) " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); } - InternalVeteranReward ivr = (*iter); + auto &ivr = (*iter); ItemInst *claim = database.CreateItem(ivr.items[0].item_id, ivr.items[0].charges); - if(!claim) { + if (!claim) { Save(); return true; } bool lore_conflict = CheckLoreConflict(claim->GetItem()); - for(int y = 1; y < 8; y++) - if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { + for (int y = 1; y < 8; y++) + if (ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges); - if(item_temp) { - if(CheckLoreConflict(item_temp->GetItem())) { + if (item_temp) { + if (CheckLoreConflict(item_temp->GetItem())) { lore_conflict = true; DuplicateLoreMessage(ivr.items[y].item_id); } - claim->PutItem(y-1, *item_temp); + claim->PutItem(y - 1, *item_temp); } } - if(lore_conflict) { + if (lore_conflict) { safe_delete(claim); return true; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 5205942b2..f93f83018 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -13793,41 +13793,32 @@ void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) { - if (app->size < sizeof(VeteranClaimRequest)) - { - Log.Out(Logs::General, Logs::None, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, sizeof(VeteranClaimRequest)); + if (app->size < sizeof(VeteranClaim)) { + Log.Out(Logs::General, Logs::None, + "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, + sizeof(VeteranClaim)); DumpPacket(app); return; } - VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; + VeteranClaim *vcr = (VeteranClaim *)app->pBuffer; - if (vcr->claim_id == 0xFFFFFFFF) //request update packet - { + if (vcr->claim_id == 0xFFFFFFFF) { // request update packet SendRewards(); + return; } - else //try to claim something! - { - if (!TryReward(vcr->claim_id)) - { - Message(13, "Your claim has been rejected."); - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = -1; - FastQueuePacket(&vetapp); - } - else - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = 0; - FastQueuePacket(&vetapp); - } - } + // try to claim something! + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaim)); + VeteranClaim *cr = (VeteranClaim *)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + + if (!TryReward(vcr->claim_id)) + cr->action = 1; + else + cr->action = 0; + + FastQueuePacket(&vetapp); } void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app)