From 9e5bfabf9195771d20d8ee5943a80d68fd3a8eab Mon Sep 17 00:00:00 2001 From: Shendare Date: Mon, 9 Nov 2015 22:43:25 -0800 Subject: [PATCH 1/3] Augmentation Feature Patch RoF+ clients now support the built-in adding, swapping, destroying, and removing of augments in equipment, updating an equipped item's look in case of ornamentation changes. All clients will now verify that the proper distiller (or a perfected distiller for RoF+) is being sent for consumption for safely removing augments. Hard-coded item IDs for distillers have been replaced with checks on item types. --- zone/client_packet.cpp | 323 ++++++++++++++++++++++++++++------------- zone/tradeskills.cpp | 27 ++-- 2 files changed, 240 insertions(+), 110 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2ee01fe25..7d392ec37 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2931,139 +2931,260 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) { ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); + //Log.Out(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i", + // in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); - // Adding augment - if (in_augment->augment_action == 0) + ItemInst *tobe_auged = nullptr, *new_aug = nullptr, *old_aug = nullptr, *aug = nullptr, *solvent = nullptr; + Inventory& user_inv = GetInv(); + + uint16 item_slot = in_augment->container_slot; + uint16 solvent_slot = in_augment->augment_slot; + uint8 mat = Inventory::CalcMaterialFromSlot(item_slot); // for when player is augging a piece of equipment while they're wearing it + + if (item_slot == INVALID_INDEX || solvent_slot == INVALID_INDEX) { - ItemInst *tobe_auged = nullptr, *auged_with = nullptr; - int8 slot = -1; - Inventory& user_inv = GetInv(); + Message(13, "Error: Invalid Aug Index."); + return; + } - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; - if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) + tobe_auged = user_inv.GetItem(item_slot); + solvent = user_inv.GetItem(solvent_slot); + new_aug = user_inv.GetItem(MainCursor); + + if (!tobe_auged) + { + Message(13, "Error: Invalid item passed for augmenting."); + return; + } + + if ((in_augment->augment_action == 1) || (in_augment->augment_action == 2)) + { + // Check for valid distiller if safely removing / swapping an augmentation + + if (!solvent) { - Message(13, "Error: Invalid Aug Index."); + Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment without a distiller."); + Message(13, "Error: Missing an augmentation distiller for safely removing this augment."); return; } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(MainCursor); - - if (tobe_auged && auged_with) + else if (solvent->GetItem()->ItemType == ItemUseTypes::ItemTypeAugmentationDistiller) { - if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && - (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) + old_aug = tobe_auged->GetAugment(in_augment->augment_index); + + if (!old_aug) { - tobe_auged->PutAugment(in_augment->augment_index, *auged_with); - tobe_auged->UpdateOrnamentationInfo(); + Log.Out(Logs::General, Logs::Error, "Player tried to safely remove a nonexistent augment."); + Message(13, "Error: No augment found in slot %i for safely removing.", in_augment->augment_index); + return; + } + else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller) + { + Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment with the wrong distiller (item %u vs expected %u).", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller); + Message(13, "Error: Wrong augmentation distiller for safely removing this augment."); + return; + } + } + else if (solvent->GetItem()->ItemType != ItemUseTypes::ItemTypePerfectedAugmentationDistiller) + { + Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment with a non-distiller item."); + Message(13, "Error: Invalid augmentation distiller for safely removing this augment."); + return; + } + } - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if (aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else + switch (in_augment->augment_action) + { + case 0: // Adding an augment + case 2: // Swapping augment + if (new_aug) + { + if (((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) && + (tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots))) { - Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); - return; - } - - itemOneToPush = tobe_auged->Clone(); - // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(MainCursor, 0, true); - - if (PutItemInInventory(slot_id, *itemOneToPush, true)) + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + if (old_aug) { + // An old augment was removed in order to be replaced with the new one (augment_action 2) + CalcBonuses(); - // Successfully added an augment to the item + + std::vector args; + args.push_back(old_aug); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + args.push_back(false); + + parse->EventItem(EVENT_AUGMENT_REMOVE, this, old_aug, nullptr, "", in_augment->augment_index, &args); + } + + tobe_auged->PutAugment(in_augment->augment_index, *new_aug); + tobe_auged->UpdateOrnamentationInfo(); + + aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) + { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not properly insert augmentation into augment slot %i. Aborting.", in_augment->augment_index); + return; + } + + itemOneToPush = tobe_auged->Clone(); + if (old_aug) + { + itemTwoToPush = old_aug->Clone(); + } + + // Must push items after the items in inventory are deleted - necessary due to lore items... + if (itemOneToPush) + { + DeleteItemInInventory(item_slot, 0, true); + DeleteItemInInventory(MainCursor, new_aug->IsStackable() ? 1 : 0, true); + + if (solvent) + { + // Consume the augment distiller + DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); + } + + if (itemTwoToPush) + { + // Return the old aug to the player's cursor + + PutItemInInventory(MainCursor, *itemTwoToPush, true); + } + + if (PutItemInInventory(item_slot, *itemOneToPush, true)) + { + CalcBonuses(); + // Successfully added an augment to the item + if (mat != _MaterialInvalid) + { + SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. + } + return; + } + else + { + Message(13, "Error: No available slot for end result. Please free up the augment slot."); + } + return; } else { - Message(13, "Error: No available slot for end result. Please free up the augment slot."); + Message(13, "Error in cloning item for augment. Aborted."); } } else { - Message(13, "Error in cloning item for augment. Aborted."); + Message(13, "Error: No available slot for augment in that item."); } + } + break; + case 1: // Removing augment safely (distiller) + aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) + { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + args.assign(1, tobe_auged); + + args.push_back(false); + + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); } else { - Message(13, "Error: No available slot for augment in that item."); + Message(13, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index); + return; } - } - } - else if (in_augment->augment_action == 1) - { - ItemInst *tobe_auged = nullptr, *auged_with = nullptr; - int8 slot = -1; - Inventory& user_inv = GetInv(); + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + tobe_auged->UpdateOrnamentationInfo(); - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot - if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(aug_slot_id); - - ItemInst *old_aug = nullptr; - if (!auged_with) - return; - const uint32 id = auged_with->GetID(); - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if (aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - - args.push_back(false); - - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting."); - return; - } - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - tobe_auged->UpdateOrnamentationInfo(); - - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); - if (itemOneToPush && itemTwoToPush && auged_with) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); - - if (!PutItemInInventory(slot_id, *itemOneToPush, true)) + itemOneToPush = tobe_auged->Clone(); + if (old_aug) + itemTwoToPush = old_aug->Clone(); + if (itemOneToPush && itemTwoToPush) { - Message(15, "Failed to remove augment properly!"); + DeleteItemInInventory(item_slot, 0, true); + DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); + + if (!PutItemInInventory(item_slot, *itemOneToPush, true)) + { + Message(15, "Failed to remove augment properly!"); + } + + if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + { + CalcBonuses(); + //Message(15, "Successfully removed an augmentation!"); + if (mat != _MaterialInvalid) + { + SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. + } + } + } + break; + case 3: // Destroying augment (formerly done in birdbath/sealer with a solvent) + + // RoF client does not require an augmentation solvent for destroying an augmentation in an item. + // Augments can be destroyed with a right click -> Destroy at any time. + + aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) + { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + + args.push_back(true); + + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation to remove at index %i. Aborting."); + return; } - if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + tobe_auged->DeleteAugment(in_augment->augment_index); + tobe_auged->UpdateOrnamentationInfo(); + + itemOneToPush = tobe_auged->Clone(); + if (itemOneToPush) { - CalcBonuses(); - //Message(15, "Successfully removed an augmentation!"); + DeleteItemInInventory(item_slot, 0, true); + + if (!PutItemInInventory(item_slot, *itemOneToPush, true)) + { + Message(15, "Failed to destroy augment properly!"); + } } - } + + CalcBonuses(); + //Message(15, "Successfully removed an augmentation!"); + if (mat != _MaterialInvalid) + { + SendWearChange(mat); + } + break; + default: // Unknown + Log.Out(Logs::General, Logs::Inventory, "Unrecognized augmentation action - cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i", + in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); + break; } } else diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 903c7c34c..2efed1e93 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -166,25 +166,34 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme else { ItemInst *old_aug = nullptr; - const uint32 id = auged_with->GetID(); + bool isSolvent = auged_with->GetItem()->ItemType == ItemUseTypes::ItemTypeAugmentationSolvent; + if (!isSolvent && auged_with->GetItem()->ItemType != ItemUseTypes::ItemTypeAugmentationDistiller) + { + Log.Out(Logs::General, Logs::Error, "Player tried to remove an augment without a solvent or distiller."); + user->Message(13, "Error: Missing an augmentation solvent or distiller for removing this augment."); + + return; + } + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_slot); - if(aug) { + if (aug) { + if (!isSolvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller) + { + Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment with the wrong distiller (item %u vs expected %u).", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller); + user->Message(13, "Error: Wrong augmentation distiller for safely removing this augment."); + return; + } std::vector args; args.push_back(aug); parse->EventItem(EVENT_UNAUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args); args.assign(1, tobe_auged); - bool destroyed = false; - if(id == 40408 || id == 40409 || id == 40410) { - destroyed = true; - } - - args.push_back(&destroyed); + args.push_back(&isSolvent); parse->EventItem(EVENT_AUGMENT_REMOVE, user, aug, nullptr, "", slot, &args); } - if(id == 40408 || id == 40409 || id == 40410) + if (isSolvent) tobe_auged->DeleteAugment(in_augment->augment_slot); else old_aug = tobe_auged->RemoveAugment(in_augment->augment_slot); From f6c2c07a941ce75dbccf0ca9e82e1df0d89d239b Mon Sep 17 00:00:00 2001 From: Shendare Date: Tue, 10 Nov 2015 18:14:47 -0800 Subject: [PATCH 2/3] Augmentation Feature Patch Code Cleanup Avoided an extraneous GetItem() call when performing an action that doesn't return an augment to the player. Added additional error checking and logging. Improved error messages and code comments. --- zone/client_packet.cpp | 71 +++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 7d392ec37..36c4e1da8 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2924,7 +2924,6 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) return; } - // Delegate to tradeskill object to perform combine AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; bool deleteItems = false; if (GetClientVersion() >= ClientVersion::RoF) @@ -2934,7 +2933,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) //Log.Out(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i", // in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); - ItemInst *tobe_auged = nullptr, *new_aug = nullptr, *old_aug = nullptr, *aug = nullptr, *solvent = nullptr; + ItemInst *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr; Inventory& user_inv = GetInv(); uint16 item_slot = in_augment->container_slot; @@ -2949,7 +2948,6 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) tobe_auged = user_inv.GetItem(item_slot); solvent = user_inv.GetItem(solvent_slot); - new_aug = user_inv.GetItem(MainCursor); if (!tobe_auged) { @@ -2996,7 +2994,15 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) { case 0: // Adding an augment case 2: // Swapping augment - if (new_aug) + new_aug = user_inv.GetItem(MainCursor); + + if (!new_aug) // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x. + { + Log.Out(Logs::General, Logs::Error, "AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor."); + Message(13, "Error: No augment found on cursor for inserting."); + return; + } + else { if (((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) && (tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots))) @@ -3014,7 +3020,6 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) args.assign(1, tobe_auged); args.push_back(false); - parse->EventItem(EVENT_AUGMENT_REMOVE, this, old_aug, nullptr, "", in_augment->augment_index, &args); } @@ -3057,27 +3062,29 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (itemTwoToPush) { - // Return the old aug to the player's cursor - - PutItemInInventory(MainCursor, *itemTwoToPush, true); + // This is a swap. Return the old aug to the player's cursor. + if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + { + Log.Out(Logs::General, Logs::Error, "Problem returning old augment to player's cursor after augmentation swap."); + Message(15, "Error: Failed to retrieve old augment after augmentation swap!"); + } } if (PutItemInInventory(item_slot, *itemOneToPush, true)) { - CalcBonuses(); // Successfully added an augment to the item + + CalcBonuses(); + if (mat != _MaterialInvalid) { SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. } - return; } else { Message(13, "Error: No available slot for end result. Please free up the augment slot."); } - - return; } else { @@ -3109,37 +3116,49 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) Message(13, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index); return; } + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); tobe_auged->UpdateOrnamentationInfo(); itemOneToPush = tobe_auged->Clone(); if (old_aug) itemTwoToPush = old_aug->Clone(); + if (itemOneToPush && itemTwoToPush) { - DeleteItemInInventory(item_slot, 0, true); + // Consume the augment distiller DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); + // Remove the augmented item + DeleteItemInInventory(item_slot, 0, true); + + // Replace it with the unaugmented item if (!PutItemInInventory(item_slot, *itemOneToPush, true)) { - Message(15, "Failed to remove augment properly!"); + Log.Out(Logs::General, Logs::Error, "Problem returning equipment item to player's inventory after safe augment removal."); + Message(15, "Error: Failed to return item after de-augmentation!"); } - if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + CalcBonuses(); + + if (mat != _MaterialInvalid) { - CalcBonuses(); - //Message(15, "Successfully removed an augmentation!"); - if (mat != _MaterialInvalid) - { - SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. - } + SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. + } + + // Drop the removed augment on the player's cursor + if (!PutItemInInventory(MainCursor, *itemTwoToPush, true)) + { + Log.Out(Logs::General, Logs::Error, "Problem returning augment to player's cursor after safe removal."); + Message(15, "Error: Failed to return augment after removal from item!"); + return; } } break; case 3: // Destroying augment (formerly done in birdbath/sealer with a solvent) - // RoF client does not require an augmentation solvent for destroying an augmentation in an item. - // Augments can be destroyed with a right click -> Destroy at any time. + // RoF client does not require an augmentation solvent for destroying an augmentation in an item. + // Augments can be destroyed with a right click -> Destroy at any time. aug = tobe_auged->GetAugment(in_augment->augment_index); if (aug) @@ -3170,12 +3189,13 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (!PutItemInInventory(item_slot, *itemOneToPush, true)) { - Message(15, "Failed to destroy augment properly!"); + Log.Out(Logs::General, Logs::Error, "Problem returning equipment item to player's inventory after augment deletion."); + Message(15, "Error: Failed to return item after destroying augment!"); } } CalcBonuses(); - //Message(15, "Successfully removed an augmentation!"); + if (mat != _MaterialInvalid) { SendWearChange(mat); @@ -3189,6 +3209,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) } else { + // Delegate to tradeskill object to perform combine Object::HandleAugmentation(this, in_augment, m_tradeskill_object); } return; From 7c414728773f3a756bd9ad6960eb7f1ffb8b5865 Mon Sep 17 00:00:00 2001 From: Shendare Date: Sat, 14 Nov 2015 11:40:28 -0800 Subject: [PATCH 3/3] Fixed swapping error message You now get the error message if the augment swap doesn't work, instead of when it works. One little bang. --- 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 36c4e1da8..b42b53e04 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3063,7 +3063,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (itemTwoToPush) { // This is a swap. Return the old aug to the player's cursor. - if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + if (!PutItemInInventory(MainCursor, *itemTwoToPush, true)) { Log.Out(Logs::General, Logs::Error, "Problem returning old augment to player's cursor after augmentation swap."); Message(15, "Error: Failed to retrieve old augment after augmentation swap!");