|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
08-10-2012, 06:29 AM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
COMMITTED: CSD Bugged Corpse Patch
After some very trying times with the 'RespawnFromHover' option, I finally came up with something that should address
the client lock-out associated with it and not cause any CSD's.
I've tested this particular patch numerous times and found no discontinuity in memory vs. database vs. corpse data.
[CSD Bugged Corpse Patch]
Code:
Index: inventory.cpp
===================================================================
--- inventory.cpp (revision 2175)
+++ inventory.cpp (working copy)
@@ -397,9 +397,11 @@
LogFile->write(EQEMuLog::Debug, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false");
#endif
- if(!m_inv[slot_id]) {
+ // Added 'IsSlotValid(slot_id)' check to both segments of client packet processing.
+ // Cursor queue slots were slipping through and crashing client
+ if(!m_inv[slot_id]) {
// Make sure the client deletes anything in this slot to match the server.
- if(client_update) {
+ if(client_update && IsValidSlot(slot_id)) {
EQApplicationPacket* outapp;
outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct));
DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
@@ -427,7 +429,7 @@
database.SaveInventory(character_id, inst, slot_id);
}
- if(client_update) {
+ if(client_update && IsValidSlot(slot_id)) {
EQApplicationPacket* outapp;
if(inst) {
if(!inst->IsStackable() && !isDeleted)
@@ -437,13 +439,13 @@
// Stackable, arrows, etc ? Delete one from the stack
outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(MoveItem_Struct));
- DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
- delitem->from_slot = slot_id;
- delitem->to_slot = 0xFFFFFFFF;
- delitem->number_in_stack = 0xFFFFFFFF;
- for(int loop=0;loop<quantity;loop++)
- QueuePacket(outapp);
- safe_delete(outapp);
+ DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
+ delitem->from_slot = slot_id;
+ delitem->to_slot = 0xFFFFFFFF;
+ delitem->number_in_stack = 0xFFFFFFFF;
+ for(int loop=0;loop<quantity;loop++)
+ QueuePacket(outapp);
+ safe_delete(outapp);
}
else {
outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct));
Index: PlayerCorpse.cpp
===================================================================
--- PlayerCorpse.cpp (revision 2175)
+++ PlayerCorpse.cpp (working copy)
@@ -352,13 +352,20 @@
pp->silver = 0;
pp->gold = 0;
pp->platinum = 0;
+
+ // need to tell client that cash has changed... issue looks like a money duplication error,
+ // but server value is actually correct. (think it is 'RespawnFromHover' related.)
// get their tints
memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint));
// solar: TODO soulbound items need not be added to corpse, but they need
// to go into the regular slots on the player, out of bags
-
+
+ // personal and cursor bag slots (251-340) are moved to corpse..should be deleting db entries
+ // too. reworked code to return and merge a list for the delete query builder instead of adding
+ // MoveItemToCorpse code to each loop.
+
// worn + inventory + cursor
std::list<uint32> removed_list;
bool cursor = false;
@@ -367,23 +374,26 @@
item = client->GetInv().GetItem(i);
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
- MoveItemToCorpse(client, item, i);
- removed_list.push_back(i);
+ removed_list.merge(MoveItemToCorpse(client, item, i));
}
}
// cursor queue
- iter_queue it;
- for(it=client->GetInv().cursor_begin(),i=8000; it!=client->GetInv().cursor_end(); it++,i++) {
- item = *it;
- if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
- {
- MoveItemToCorpse(client, item, i);
- cursor = true;
+ if (!RuleB(Character, RespawnFromHover)) {
+ // bumped starting assignment to 8001 because any in-memory 'slot 8000' item was moved above as 'slot 30'
+ // this was mainly for client profile state reflection..should match db player inventory entries now.
+ iter_queue it;
+ for(it=client->GetInv().cursor_begin(),i=8001; it!=client->GetInv().cursor_end(); it++,i++) {
+ item = *it;
+ if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
+ {
+ removed_list.merge(MoveItemToCorpse(client, item, i));
+ cursor = true;
+ }
}
}
- if(removed_list.size() != 0) {
+ if(removed_list.size() != 0) {
std::stringstream ss("");
ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID();
ss << " AND (";
@@ -401,14 +411,33 @@
ss << ")";
database.RunQuery(ss.str().c_str(), ss.str().length());
}
-
+
+ // some of this code is redundent now..hover rule check in cursor queue should eliminate some code's activation.
if(cursor) {
- std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
- std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
- database.SaveCursor(client->CharacterID(),
- start, finish);
+ while(!client->GetInv().CursorEmpty()) {
+ if (RuleB(Character, RespawnFromHover)) {
+ client->Message(0, "Attempting to delete %s from cursor.", client->GetInv().GetItem(SLOT_CURSOR)->GetItem()->Name);
+ client->DeleteItemInInventory(SLOT_CURSOR, 0, true, false);
+ }
+ else {
+ client->DeleteItemInInventory(SLOT_CURSOR, 0, false, false); // no need to update client without hover
+ }
+ }
+ // you shouldn't see these messages unless 'RespawnFromHover' rule is changed between cursor queue process and here...
+ if (RuleB(Character, RespawnFromHover)) {
+ client->Message(13, "Warning: Your cursor may contain duplicate items not found on the server!");
+ client->Message(0, "Place these items into an empty inventory slot to resolve this problem.");
+ }
}
+ else {
+ if(RuleB(Character, RespawnFromHover) && !client->GetInv().CursorEmpty()) {
+ std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
+ std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
+ database.SaveCursor(client->CharacterID(), start, finish);
+ }
+ }
+ // client->CalcBonuses(); // will only affect offline profile viewing for dead characters..otherwise unneeded overhead
client->Save();
} //end "not leaving naked corpses"
@@ -417,28 +446,35 @@
}
// solar: helper function for client corpse constructor
-void Corpse::MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot)
+std::list<uint32> Corpse::MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot)
{
int bagindex;
sint16 interior_slot;
ItemInst *interior_item;
+ std::list<uint32> returnlist;
AddItem(item->GetItem()->ID, item->GetCharges(), equipslot, item->GetAugmentItemID(0), item->GetAugmentItemID(1), item->GetAugmentItemID(2), item->GetAugmentItemID(3), item->GetAugmentItemID(4));
- if(item->IsType(ItemClassContainer))
+ returnlist.push_back(equipslot);
+
+ // Qualified bag slot iterations. processing bag slots that don't exist is probably not a good idea.
+ if(item->IsType(ItemClassContainer) && ((equipslot >= 22 && equipslot <=30))) // Limit the bag check to inventory and cursor slots.
{
for(bagindex = 0; bagindex <= 10; bagindex++)
{
+ // For empty bags in cursor queue, slot was being resolved as SLOT_INVALID (-1)
interior_slot = Inventory::CalcSlotId(equipslot, bagindex);
interior_item = client->GetInv().GetItem(interior_slot);
if(interior_item)
{
AddItem(interior_item->GetItem()->ID, interior_item->GetCharges(), interior_slot, interior_item->GetAugmentItemID(0), interior_item->GetAugmentItemID(1), interior_item->GetAugmentItemID(2), interior_item->GetAugmentItemID(3), interior_item->GetAugmentItemID(4));
+ returnlist.push_back(Inventory::CalcSlotId(equipslot, bagindex));
client->DeleteItemInInventory(interior_slot, 0, true, false);
}
}
}
client->DeleteItemInInventory(equipslot, 0, true, false);
+ return returnlist;
}
// To be called from LoadFromDBData
@@ -981,19 +1017,43 @@
ItemList::iterator cur,end;
cur = itemlist.begin();
end = itemlist.end();
+
+ // Observed some odd behavior concerning the last corpse slot as coded (SoF client in this case.)
+ // Clicking index 29 (zero-based) with an item in index 28 results in retrieving index 28 item.
+ // Clicking index 29 with no item in index 28 results in the closing of the corpse loot window.
+
+ int corpselootlimit = 30; // 30 is the original value // con check value in QueryLoot needs to reflect this value
+ /* need actual corpse limit values per client (or client range)..if always 30, then these con checks are unneeded
+ // enumeration shouldn't be needed unless someone finds a use for this info elsewhere
+ if (client->GetClientVersion()>=EQClientVoA)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientHoT)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientUnderfoot)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientSoD)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientSoF) // SoF has 32 visible slots..change untested
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientTitanium)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClient62)
+ corpselootlimit=30;
+ else
+ corpselootlimit=30; // */
+
for(; cur != end; cur++) {
ServerLootItem_Struct* item_data = *cur;
item_data->lootslot = 0xFFFF;
+
+ // Dont display the item if it's in a bag
- // Dont display the item if it's in a bag
- if(!IsPlayerCorpse() || item_data->equipSlot <= 30 || tCanLoot>=3)
+ // added cursor queue slots to corpse item visibility list. Nothing else should be making it to corpse.
+ if(!IsPlayerCorpse() || item_data->equipSlot <= 30 || tCanLoot>=3 ||
+ (item_data->equipSlot >= 8000 && item_data->equipSlot <= 8999))
{
- if (i >= 30)
+ if (i < corpselootlimit) // < 30 (0 - 29)
{
- Message(13, "Warning: Too many items to display. Loot some then re-loot the corpse to see the rest");
- }
- else
- {
item = database.GetItem(item_data->item_id);
if (client && item)
{
@@ -1006,9 +1066,24 @@
item_data->lootslot = i;
}
}
+ else if (i == corpselootlimit) // = 30
+ {
+ client->Message(13, "*** This corpse contains more items than can be displayed! ***");
+ client->Message(0, "Remove items and re-loot corpse to access remaining inventory.");
+ }
i++;
}
}
+ if (i > corpselootlimit) // > 30 (remember 'i' is increased again after the last iteration, so no '=')
+ client->Message(0, "(%s contains %i additional %s.)", GetName(), (i-corpselootlimit), (i-corpselootlimit)==1?"item":"items");
+
+ if (IsPlayerCorpse() && i == 0 && itemlist.size() > 0) { // somehow, corpse contains items, but client doesn't see them...
+ client->Message(13, "This corpse contains items that you do not have permission to access!");
+ client->Message(0, "Contact a GM for assistance to see if item replacement is necessary.");
+ client->Message(0, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetDBID(), GetName(), itemlist.size());
+ // if needed/wanted - create log dump->iterate corpse list..need pointer to log file
+ // could add code to check for owning client and give list of bugged items on corpse
+ }
}
// Disgrace: Client seems to require that we send the packet back...
Index: PlayerCorpse.h
===================================================================
--- PlayerCorpse.h (revision 2175)
+++ PlayerCorpse.h (working copy)
@@ -111,7 +111,7 @@
inline int GetRezzExp() { return rezzexp; }
protected:
- void MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot);
+ std::list<uint32> MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot);
private:
bool p_PlayerCorpse;
Change to Client:eleteItemInInventory:
Added IsValidSlot() checks to client updates. When invalid slots were being sent, the client was crashing.
Change to Corpse::Corpse(Client):
Added discrimantor for 'RespawnFromHover' in the cursor queue processor. This matches the client behavior in this mode
and still allows for full cursor removal with RespawnFromHover = false. The money issue was not addressed in this patch
and I will leave that to someone else.
Change to Corpse::MoveItemToCorpse:
Changed the procedure to a return function of std::list<uint32> and qualified the bag slot check to only check bags
in the 251 to 340 range (inventory slots 22 to 30.) Otherwise, cursor queue bags slots were being processed for items..and
we know those don't exist...
Change to Corpse::MakeLootRequestPacket:
Added cursor queue slot range to packet builder. Should alleviate the cursor queue items that are hidden on a corpse and
making it bugged. Original coding allowed these items to be placed on a corpse, but not be seen by a client when they
looted the corpse. (CSD Support Patch contains a new #corpse sub-command that allows inspection of a corpse's
contents. Based on the iterations in Corpse::Corpse(Client...), there shouldn't be any additional ranges on a corpse.)
There is some outdated code that I left in place because this patch currently works and I didn't want to spend additional
time with it. It shouldn't affect server operation and worked as intended prior to adding the cursor queue
RespawnFromHover code.
I consider this patch full BETA, but it should be tested out on test servers before full implementation due to the nature
of the affected code.
__________________
Uleat of Bertoxxulous
Compilin' Dirty
Last edited by cavedude; 09-13-2012 at 01:49 PM..
|
|
|
|
08-10-2012, 03:26 PM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
I let slide the possible exclusion from 'RespawnFromHover' code and clients less than SoF.
There should be no issue with Hover off, but I need someone who can test this with a lower than SoF client to report
back if they are getting duplicate cursor queue items and/or CSD's as a result of server rule 'RespawnFromHover' = true
and client death with queue items.
('#peekinv all' should show CSD's before messing up inventory with swaps.)
Thanks!
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|
|
|
|
08-11-2012, 07:03 AM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
Here is the replacement for the method that handles the 'RespawnFromHover' issue. It is currently set to recognize SoF and
higher clients when hover is set to true. This client was noted in the PEQ database and this is coded as such.
This is a direct replacement fix.
[PlayerCorpse.cpp]
Code:
Corpse::Corpse(Client* client, sint32 in_rezexp)
// vesuvias - appearence fix
: Mob
(
"Unnamed_Corpse",
"",
0,
0,
client->GetGender(),
client->GetRace(),
client->GetClass(),
BT_Humanoid, // bodytype added
client->GetDeity(),
client->GetLevel(),
0,
client->GetSize(),
0,
client->GetHeading(), // heading
client->GetX(),
client->GetY(),
client->GetZ(),
0,
client->GetTexture(),
client->GetHelmTexture(),
0, // AC
0,
0,
0,
0,
0,
0,
0,
0, // CHA
client->GetPP().haircolor,
client->GetPP().beardcolor,
client->GetPP().eyecolor1,
client->GetPP().eyecolor2,
client->GetPP().hairstyle,
client->GetPP().face,
client->GetPP().beard,
client->GetPP().drakkin_heritage,
client->GetPP().drakkin_tattoo,
client->GetPP().drakkin_details,
0,
0xff, // aa title
0,
0,
0,
0,
0,
0,
0, // qglobal
0, // maxlevel
0 // scalerate
),
corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)),
corpse_res_timer(RuleI(Character, CorpseResTimeMS)),
corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)),
corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)),
loot_cooldown_timer(10)
{
int i;
PlayerProfile_Struct *pp = &client->GetPP();
ItemInst *item;
if(!zone->HasGraveyard()) {
corpse_graveyard_timer.Disable();
}
memset(item_tint, 0, sizeof(item_tint));
for (i=0; i<MAX_LOOTERS; i++)
looters[i] = 0;
pIsChanged = true;
rezzexp = in_rezexp;
can_rez = true;
p_PlayerCorpse = true;
pLocked = false;
BeingLootedBy = 0xFFFFFFFF;
charid = client->CharacterID();
dbid = 0;
p_depop = false;
copper = 0;
silver = 0;
gold = 0;
platinum = 0;
strcpy(orgname, pp->name);
strcpy(name, pp->name);
//become_npc was not being initialized which led to some pretty funky things with newly created corpses
become_npc = false;
SetPKItem(0);
if(!RuleB(Character, LeaveNakedCorpses) || RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) {
// cash
SetCash(pp->copper, pp->silver, pp->gold, pp->platinum);
pp->copper = 0;
pp->silver = 0;
pp->gold = 0;
pp->platinum = 0;
// need to tell client that cash has changed... issue looks like a money duplication error,
// but server value is actually correct. (think it is 'RespawnFromHover' related.)
// get their tints
memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint));
// solar: TODO soulbound items need not be added to corpse, but they need
// to go into the regular slots on the player, out of bags
// personal and cursor bag slots (251-340) are moved to corpse..should be deleting db entries
// too. reworked code to return and merge a list for the delete query builder instead of adding
// MoveItemToCorpse code to each loop.
// worn + inventory + cursor
std::list<uint32> removed_list;
bool cursor = false;
for(i = 0; i <= 30; i++)
{
item = client->GetInv().GetItem(i);
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
removed_list.merge(MoveItemToCorpse(client, item, i));
}
}
// cursor queue // change to first client that supports 'death hover' mode, if not SoF.
if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < EQClientSoF) {
// bumped starting assignment to 8001 because any in-memory 'slot 8000' item was moved above as 'slot 30'
// this was mainly for client profile state reflection..should match db player inventory entries now.
iter_queue it;
for(it=client->GetInv().cursor_begin(),i=8001; it!=client->GetInv().cursor_end(); it++,i++) {
item = *it;
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
removed_list.merge(MoveItemToCorpse(client, item, i));
cursor = true;
}
}
}
if(removed_list.size() != 0) {
std::stringstream ss("");
ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID();
ss << " AND (";
std::list<uint32>::const_iterator iter = removed_list.begin();
bool first = true;
while(iter != removed_list.end()) {
if(first) {
first = false;
} else {
ss << " OR ";
}
ss << "slotid=" << (*iter);
iter++;
}
ss << ")";
database.RunQuery(ss.str().c_str(), ss.str().length());
}
if(cursor) { // all cursor items should be on corpse (client < SoF or RespawnFromHover = false)
while(!client->GetInv().CursorEmpty())
client->DeleteItemInInventory(SLOT_CURSOR, 0, false, false);
}
else { // only visible cursor made it to corpse (client >= Sof and RespawnFromHover = true)
std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
database.SaveCursor(client->CharacterID(), start, finish);
}
// client->CalcBonuses(); // will only affect offline profile viewing of dead characters..unneeded overhead
client->Save();
} //end "not leaving naked corpses"
Rezzed(false);
Save();
}
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|
|
|
|
|
|
|
08-12-2012, 07:35 AM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
My first meanderings with this issue found that the client does not like to be updated when in 'Death Hover' mode.
I finally found a way around it by letting the client do what it wants to do in that particular mode, and everything
seemed to be ok through all of my testing..that is, until now...
I found another little sneaky bug that SOE snuck in the client.
If you die in non-Death Hover (return to bind point on death) with items in your cursor queue, and your last zone reload
was from a different zone (this can even be the staging area), then the very last item in your cursor queue manages to
follow you. Since the server has already moved your inventory to your corpse, you're left with a 'phantom' object.
If you die in the same zone as your bind point afterwards, the cursor queue is cleared properly..hence why this bug didn't
show itself right away. (My testing was all done at my bind point and my first #kill was always with a clear cursor.)
The code works as it is intended, but I need to do some more research into this. It could be that a flag needs to be set in
one of the 'unknown' fields in the packet structure, but that will take some experimenting.
In Death Hover mode, there aren't any apparent problems, and this bug fixes itself when you try to put the 'phantom'
item in your inventory (there is no 'error -> deleting item' message.)
I'll play with this for now and see what I can find out.
The fix is still good and only applies to player death with more than one item on their cursor. As long as the player under-
stands that they need to clear this item before attempting to move items around, there shouldn't be an issue with CSD's.
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|
|
|
|
08-12-2012, 09:47 AM
|
Dragon
|
|
Join Date: May 2010
Posts: 965
|
|
would you please just always post things as a diff. if you post a diff i can just save as a file and apply it. having to poke through the cpp and replace an entire function manually is a pain in the ass especially if I have other changes to said function.
|
08-12-2012, 04:22 PM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
Sorry, wasn't trying to make this difficult...
So, you prefer an actual .diff file over a .patch file then?
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|
08-12-2012, 05:47 PM
|
Demi-God
|
|
Join Date: Aug 2010
Posts: 1,742
|
|
Diff and patch are the same thing. I believe Sorvani was referring to the recent "direct replacement" post.
|
08-14-2012, 05:27 AM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
Aye, he was. I did the same thing over in the Support Patch as well to add an update on that one.
I can't create an additional patch from a working copy without including the original patch again.
Should I just make one diff file with both patches and both updates and just wait to see if it gets committed before
moving on to the next steps?
I was probably trying to keep it too separate and making it more difficult that it needed be...
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|
08-14-2012, 10:22 AM
|
Dragon
|
|
Join Date: May 2010
Posts: 965
|
|
when i am testing a change and want someone to try a diff i have a second copy of the SVN checked out that I would use to copy/paste my working .cpp files into to create diffs against the current SVN and then revert it back to clean SVN. and then make the next change, etc.
You are messing with corpses and item swapping in the client vs the server. These are some of the biggest causes of exploits in EQ. Always have been. The best way to get it tested is to provide a clean diff that someone can apply and build against.
|
|
|
|
08-14-2012, 05:49 PM
|
|
Developer
|
|
Join Date: Apr 2012
Location: North Carolina
Posts: 2,815
|
|
Ok, here is everything so far diff'd off of rev 2185.
Code:
Index: client_packet.cpp
===================================================================
--- client_packet.cpp (revision 2185)
+++ client_packet.cpp (working copy)
@@ -3310,6 +3310,55 @@
return;
}
}
+
+ // Added checks for illegal bagslot swaps..should help with certain cheats (currently checks personal and cursor bag slots.)
+ // - if a player has used an illegal inventory cheat, they can lose items to bugged corpses at some point.
+ //* Start
+ bool mi_hack = false;
+
+ if (mi->from_slot >= 251 && mi->from_slot <= 340) {
+ if (mi->from_slot > 330)
+ mi_hack = true; // why are we moving from a cursor bagslot when you can't open it?
+ else {
+ sint16 from_invslot = Inventory::CalcSlotId(mi->from_slot);
+ const ItemInst *from_invslotitem = GetInv().GetItem(from_invslot);
+
+ if (!from_invslotitem) // trying to move from bag slots when parent inventory slot is empty
+ mi_hack = true;
+ else if (from_invslotitem->GetItem()->ItemClass == 1) { // checking the parent inventory slot for container
+ if ((Inventory::CalcBagIdx(mi->from_slot) + 1) > from_invslotitem->GetItem()->BagSlots)
+ mi_hack = true; // trying to move from slots beyond parent container size
+ }
+ else // trying to move from bag slots when inventory slot item is not a container
+ mi_hack = true;
+ }
+ }
+
+ if (mi->to_slot >= 251 && mi->to_slot <= 340) {
+ if (mi->to_slot > 330)
+ mi_hack = true; // why are we moving to a cursor bagslot when you can't open it?
+ else {
+ sint16 to_invslot = Inventory::CalcSlotId(mi->to_slot);
+ const ItemInst *to_invslotitem = GetInv().GetItem(to_invslot);
+
+ if (!to_invslotitem) // trying to move into bag slots when parent inventory slot is empty
+ mi_hack = true;
+ else if (to_invslotitem->GetItem()->ItemClass == 1) { // checking the parent inventory slot for container
+ if ((Inventory::CalcBagIdx(mi->to_slot) + 1) > to_invslotitem->GetItem()->BagSlots)
+ mi_hack = true; // trying to move into slots beyond parent container size
+ }
+ else // trying to move into bag slots when inventory slot item is not a container
+ mi_hack = true;
+ }
+ }
+
+ if (mi_hack) { // a CSD can also cause this condition, but more likely the use of a cheat
+ Message(13, "Hack detected: Illegal use of inventory bag slots!");
+ // TODO: Decide whether to log player as hacker - currently has no teeth...
+ // Kick();
+ // return;
+ } // End */
+
SwapItem(mi);
return;
}
Index: command.cpp
===================================================================
--- command.cpp (revision 2185)
+++ command.cpp (working copy)
@@ -456,7 +456,8 @@
command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) ||
command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) ||
command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) ||
- command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets)
+ command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) ||
+ command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp)
)
{
command_deinit();
@@ -3035,15 +3036,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3055,15 +3058,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
if (inst && inst->IsType(ItemClassContainer)) {
@@ -3072,17 +3077,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
}
else
{
- c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
}
}
}
@@ -3091,50 +3098,73 @@
{
const ItemInst* inst = client->GetInv().GetItem(9999);
item = (inst) ? inst->GetItem() : NULL;
- c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", 9999,
+ c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", 9999,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
+
+ // Changed to show 'empty' cursors and not to show bag slots on 'queued' cursor slots (cursor bag slots 331 to 340 are not arrayed...)
+ // - was pointless to show bags on anything after slot 30[0], because it only repeated the 30[0] bag items.
if (bAll || (strcasecmp(sep->arg[1], "cursor")==0)) {
// Personal inventory items
bFound = true;
iter_queue it;
int i=0;
- for(it=client->GetInv().cursor_begin();it!=client->GetInv().cursor_end();it++,i++) {
- const ItemInst* inst = *it;
- item = (inst) ? inst->GetItem() : NULL;
+
+ if(client->GetInv().CursorEmpty()) { // Display 'front' cursor slot even if 'empty' (item(30[0]) == null)
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", SLOT_CURSOR,i,
- ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", SLOT_CURSOR,i,
+ 0, 0x12, 0, "null", 0x12, 0);
}
else
{
- c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", SLOT_CURSOR,i,
- ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", SLOT_CURSOR,i,
+ 0, 0x12, 0, "null", 0x12, 0);
}
+ }
+ else {
+ for(it=client->GetInv().cursor_begin();it!=client->GetInv().cursor_end();it++,i++) {
+ const ItemInst* inst = *it;
+ item = (inst) ? inst->GetItem() : NULL;
+ if (c->GetClientVersion() >= EQClientSoF)
+ {
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", SLOT_CURSOR,i,
+ ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
+ }
+ else
+ {
+ c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", SLOT_CURSOR,i,
+ ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
+ }
- if (inst && inst->IsType(ItemClassContainer)) {
- for (uint8 j=0; j<10; j++) {
- const ItemInst* instbag = client->GetInv().GetItem(SLOT_CURSOR, j);
- item = (instbag) ? instbag->GetItem() : NULL;
- if (c->GetClientVersion() >= EQClientSoF)
- {
- c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)",
- Inventory::CalcSlotId(SLOT_CURSOR, j),
- SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ if (inst && inst->IsType(ItemClassContainer) && i==0) { // only display contents of slot 30[0] container..higher ones don't exist
+ for (uint8 j=0; j<10; j++) {
+ const ItemInst* instbag = client->GetInv().GetItem(SLOT_CURSOR, j);
+ item = (instbag) ? instbag->GetItem() : NULL;
+ if (c->GetClientVersion() >= EQClientSoF)
+ {
+ c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i",
+ Inventory::CalcSlotId(SLOT_CURSOR, j),
+ SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
+ }
+ else
+ {
+ c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i",
+ Inventory::CalcSlotId(SLOT_CURSOR, j),
+ SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:instbag->GetCharges()));
+ }
}
- else
- {
- c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c)",
- Inventory::CalcSlotId(SLOT_CURSOR, j),
- SLOT_CURSOR, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
- }
}
}
}
@@ -3148,15 +3178,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3170,15 +3202,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
if (inst && inst->IsType(ItemClassContainer)) {
@@ -3187,17 +3221,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3207,15 +3243,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
if (inst && inst->IsType(ItemClassContainer)) {
@@ -3224,17 +3262,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
}
@@ -3248,15 +3288,17 @@
item = (inst) ? inst->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c)", i,
+ c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i,
((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
if (inst && inst->IsType(ItemClassContainer)) {
@@ -3265,17 +3307,19 @@
item = (instbag) ? instbag->GetItem() : NULL;
if (c->GetClientVersion() >= EQClientSoF)
{
- c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
else
{
- c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c)",
+ c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i",
Inventory::CalcSlotId(i, j),
i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID),
- ((item==0)?"null":item->Name), 0x12);
+ ((item==0)?"null":item->Name), 0x12,
+ ((item==0)?0:inst->GetCharges()));
}
}
@@ -3286,7 +3330,7 @@
if (!bFound)
{
c->Message(0, "Usage: #peekinv [worn|cursor|inv|bank|trade|trib|all]");
- c->Message(0, " Displays a portion of the targetted user's inventory");
+ c->Message(0, " Displays a portion of the targeted user's inventory");
c->Message(0, " Caution: 'all' is a lot of information!");
}
}
@@ -3727,6 +3771,12 @@
else
c->Message(0, "Insufficient status to modify player corpse.");
}
+ else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) {
+ if (target == 0 || !target->IsCorpse())
+ c->Message(0, "Error: Target must be a corpse.");
+ else
+ target->CastToCorpse()->QueryLoot(c);
+ }
else if (strcasecmp(sep->arg[1], "lock") == 0) {
if (target == 0 || !target->IsCorpse())
c->Message(0, "Error: Target must be a corpse.");
@@ -3777,6 +3827,7 @@
c->Message(0, " Lock - GM locks the corpse - cannot be looted by non-GM");
c->Message(0, " UnLock");
c->Message(0, " RemoveCash");
+ c->Message(0, " InspectLoot");
c->Message(0, " [to remove items from corpses, loot them]");
c->Message(0, "Lead-GM status required to delete/modify player corpses");
c->Message(0, " DeletePlayerCorpses");
@@ -11529,3 +11580,40 @@
else
t->ShowXTargets(c);
}
+
+void command_zopp(Client *c, const Seperator *sep)
+{ // - Owner only command..non-targetable to eliminate malicious or mischievious activities.
+ if (!c)
+ return;
+ if (sep->argnum < 2 || sep->argnum > 3)
+ c->Message(0, "Usage: #zopp [slot id] [item id] [*charges]");
+ else if (!sep->IsNumber(1) || !sep->IsNumber(2) || (sep->argnum == 3 && !sep->IsNumber(3)))
+ c->Message(0, "Usage: #zopp [slot id] [item id] [*charges]");
+ else {
+ sint16 slotid = atoi(sep->arg[1]);
+ int32 itemid = atoi(sep->arg[2]);
+ sint16 charges = sep->argnum == 3 ? atoi(sep->arg[3]) : 1; // defaults to 1 charge if not specified
+
+ const Item_Struct* FakeItem = database.GetItem(itemid);
+
+ if (!FakeItem) {
+ c->Message(13, "Error: Item [%u] is not a valid item id.", itemid);
+ return;
+ }
+
+ if (database.GetItemStatus(itemid) > c->Admin()) {
+ c->Message(13, "Error: Insufficient status to use this command.");
+ return;
+ }
+
+ if (charges < 0 || charges > FakeItem->StackSize) {
+ c->Message(13, "Warning: The specified charge count does not meet expected criteria!");
+ c->Message(0, "Processing request..results may cause unpredictable behavior.");
+ }
+
+ ItemInst* FakeItemInst = database.CreateItem(FakeItem, charges);
+ c->SendItemPacket(slotid, FakeItemInst, ItemPacketTrade);
+ c->Message(0, "Sending zephyr op packet to client - %s (%u) with %i %s to slot %i.", FakeItem->Name, itemid, charges, abs(charges==1)?"charge":"charges", slotid);
+ safe_delete(FakeItemInst);
+ }
+}
\ No newline at end of file
Index: command.h
===================================================================
--- command.h (revision 2185)
+++ command.h (working copy)
@@ -320,6 +320,7 @@
void command_qtest(Client *c, const Seperator *sep);
void command_mysql(Client *c, const Seperator *sep);
void command_xtargets(Client *c, const Seperator *sep);
+void command_zopp(Client *c, const Seperator *sep);
#ifdef EMBPERL
void command_embperl_plugin(Client *c, const Seperator *sep);
Index: inventory.cpp
===================================================================
--- inventory.cpp (revision 2185)
+++ inventory.cpp (working copy)
@@ -222,6 +222,14 @@
inst->SetCharges(1);
if ((inst->GetCharges()>0))
inst->SetCharges(inst->GetCharges());
+
+ // Added reduction of overcharged items to maximum stacksize
+ if ((inst->GetCharges()>inst->GetItem()->StackSize)) {
+ inst->SetCharges(inst->GetItem()->StackSize);
+ Message(0, "Your summoned item is charged beyond maximum allowable - adjusting to %i charges.", inst->GetCharges());
+ }
+
+ // Corrected the augment references to reflect augment name/id instead of base item name/id
if (aug1) {
const Item_Struct* augitem1 = database.GetItem(aug1);
if (augitem1) {
@@ -229,7 +237,7 @@
inst->PutAugment(&database, 0, aug1);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1);
}
}
}
@@ -240,7 +248,7 @@
inst->PutAugment(&database, 1, aug2);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2);
}
}
}
@@ -251,7 +259,7 @@
inst->PutAugment(&database, 2, aug3);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3);
}
}
}
@@ -262,7 +270,7 @@
inst->PutAugment(&database, 3, aug4);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4);
}
}
}
@@ -273,7 +281,7 @@
inst->PutAugment(&database, 4, aug5);
}
else {
- Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
+ Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5);
}
}
}
@@ -397,9 +405,11 @@
LogFile->write(EQEMuLog::Debug, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false");
#endif
+ // Added 'IsSlotValid(slot_id)' check to both segments of client packet processing.
+ // - cursor queue slots were slipping through and crashing client
if(!m_inv[slot_id]) {
// Make sure the client deletes anything in this slot to match the server.
- if(client_update) {
+ if(client_update && IsValidSlot(slot_id)) {
EQApplicationPacket* outapp;
outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct));
DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer;
@@ -427,7 +437,7 @@
database.SaveInventory(character_id, inst, slot_id);
}
- if(client_update) {
+ if(client_update && IsValidSlot(slot_id)) {
EQApplicationPacket* outapp;
if(inst) {
if(!inst->IsStackable() && !isDeleted)
Index: PlayerCorpse.cpp
===================================================================
--- PlayerCorpse.cpp (revision 2185)
+++ PlayerCorpse.cpp (working copy)
@@ -353,6 +353,8 @@
pp->gold = 0;
pp->platinum = 0;
+ // 'RespawnFromHover = true' adversely affects client money removal. Server amount remains correct.
+
// get their tints
memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint));
@@ -367,23 +369,28 @@
item = client->GetInv().GetItem(i);
if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
{
- MoveItemToCorpse(client, item, i);
- removed_list.push_back(i);
+ removed_list.merge(MoveItemToCorpse(client, item, i));
}
}
- // cursor queue
- iter_queue it;
- for(it=client->GetInv().cursor_begin(),i=8000; it!=client->GetInv().cursor_end(); it++,i++) {
- item = *it;
- if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
- {
- MoveItemToCorpse(client, item, i);
- cursor = true;
+ // cursor queue // change to first client that supports 'death hover' mode, if not SoF.
+ if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < EQClientSoF) {
+
+ // bumped starting assignment to 8001 because any in-memory 'slot 8000' item was moved above as 'slot 30'
+ // this was mainly for client profile state reflection..should match db player inventory entries now.
+
+ iter_queue it;
+ for(it=client->GetInv().cursor_begin(),i=8001; it!=client->GetInv().cursor_end(); it++,i++) {
+ item = *it;
+ if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent))
+ {
+ removed_list.merge(MoveItemToCorpse(client, item, i));
+ cursor = true;
+ }
}
}
- if(removed_list.size() != 0) {
+ if(removed_list.size() != 0) {
std::stringstream ss("");
ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID();
ss << " AND (";
@@ -401,14 +408,18 @@
ss << ")";
database.RunQuery(ss.str().c_str(), ss.str().length());
}
-
- if(cursor) {
- std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
- std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
- database.SaveCursor(client->CharacterID(),
- start, finish);
+
+ if(cursor) { // all cursor items should be on corpse (client < SoF or RespawnFromHover = false)
+ while(!client->GetInv().CursorEmpty())
+ client->DeleteItemInInventory(SLOT_CURSOR, 0, false, false);
}
+ else { // only visible cursor made it to corpse (client >= Sof and RespawnFromHover = true)
+ std::list<ItemInst*>::const_iterator start = client->GetInv().cursor_begin();
+ std::list<ItemInst*>::const_iterator finish = client->GetInv().cursor_end();
+ database.SaveCursor(client->CharacterID(), start, finish);
+ }
+ // client->CalcBonuses(); // will only affect offline profile viewing of dead characters..unneeded overhead
client->Save();
} //end "not leaving naked corpses"
@@ -417,28 +428,35 @@
}
// solar: helper function for client corpse constructor
-void Corpse::MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot)
+std::list<uint32> Corpse::MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot)
{
int bagindex;
sint16 interior_slot;
ItemInst *interior_item;
+ std::list<uint32> returnlist;
AddItem(item->GetItem()->ID, item->GetCharges(), equipslot, item->GetAugmentItemID(0), item->GetAugmentItemID(1), item->GetAugmentItemID(2), item->GetAugmentItemID(3), item->GetAugmentItemID(4));
- if(item->IsType(ItemClassContainer))
+ returnlist.push_back(equipslot);
+
+ // Qualified bag slot iterations. processing bag slots that don't exist is probably not a good idea.
+ if(item->IsType(ItemClassContainer) && ((equipslot >= 22 && equipslot <=30))) // Limit the bag check to inventory and cursor slots.
{
for(bagindex = 0; bagindex <= 10; bagindex++)
{
+ // For empty bags in cursor queue, slot was previously being resolved as SLOT_INVALID (-1)
interior_slot = Inventory::CalcSlotId(equipslot, bagindex);
interior_item = client->GetInv().GetItem(interior_slot);
if(interior_item)
{
AddItem(interior_item->GetItem()->ID, interior_item->GetCharges(), interior_slot, interior_item->GetAugmentItemID(0), interior_item->GetAugmentItemID(1), interior_item->GetAugmentItemID(2), interior_item->GetAugmentItemID(3), interior_item->GetAugmentItemID(4));
+ returnlist.push_back(Inventory::CalcSlotId(equipslot, bagindex));
client->DeleteItemInInventory(interior_slot, 0, true, false);
}
}
}
client->DeleteItemInInventory(equipslot, 0, true, false);
+ return returnlist;
}
// To be called from LoadFromDBData
@@ -981,19 +999,41 @@
ItemList::iterator cur,end;
cur = itemlist.begin();
end = itemlist.end();
+
+ int corpselootlimit = 30; // 30 is the original value // con check value in QueryLoot needs to reflect this value
+
+ /* need actual corpse limit values per client (or client range)..if always 30, then these con checks are unneeded
+ // enumeration shouldn't be needed unless someone finds a use for this info elsewhere
+
+ if (client->GetClientVersion()>=EQClientVoA)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientHoT)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientUnderfoot)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientSoD)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientSoF) // SoF has 32 visible slots..change untested
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClientTitanium)
+ corpselootlimit=30;
+ else if (client->GetClientVersion()>=EQClient62)
+ corpselootlimit=30;
+ else
+ corpselootlimit=30; // */
+
for(; cur != end; cur++) {
ServerLootItem_Struct* item_data = *cur;
item_data->lootslot = 0xFFFF;
+
+ // Dont display the item if it's in a bag
- // Dont display the item if it's in a bag
- if(!IsPlayerCorpse() || item_data->equipSlot <= 30 || tCanLoot>=3)
+ // Added cursor queue slots to corpse item visibility list. Nothing else should be making it to corpse.
+ if(!IsPlayerCorpse() || item_data->equipSlot <= 30 || tCanLoot>=3 ||
+ (item_data->equipSlot >= 8000 && item_data->equipSlot <= 8999))
{
- if (i >= 30)
+ if (i < corpselootlimit) // < 30 (0 - 29)
{
- Message(13, "Warning: Too many items to display. Loot some then re-loot the corpse to see the rest");
- }
- else
- {
item = database.GetItem(item_data->item_id);
if (client && item)
{
@@ -1006,9 +1046,24 @@
item_data->lootslot = i;
}
}
+ else if (i == corpselootlimit) // = 30
+ {
+ client->Message(13, "*** This corpse contains more items than can be displayed! ***");
+ client->Message(0, "Remove items and re-loot corpse to access remaining inventory.");
+ }
i++;
}
}
+ if (i > corpselootlimit) // > 30 (remember 'i' is increased again after the last iteration, so no '=')
+ client->Message(0, "(%s contains %i additional %s.)", GetName(), (i-corpselootlimit), (i-corpselootlimit)==1?"item":"items");
+
+ if (IsPlayerCorpse() && i == 0 && itemlist.size() > 0) { // somehow, corpse contains items, but client doesn't see them...
+ client->Message(13, "This corpse contains items that you do not have permission to access!");
+ client->Message(0, "Contact a GM for assistance to see if item replacement is necessary.");
+ client->Message(0, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetDBID(), GetName(), itemlist.size());
+ // if needed/wanted - create log dump->iterate corpse list..need pointer to log file
+ // could add code to check for owning client and give list of bugged items on corpse
+ }
}
// Disgrace: Client seems to require that we send the packet back...
@@ -1296,7 +1351,7 @@
}
void Corpse::QueryLoot(Client* to) {
- int x = 0;
+ int x = 0, y = 0; // x = visible items, y = total items
to->Message(0, "Coin: %ip %ig %is %ic", platinum, gold, silver, copper);
ItemList::iterator cur,end;
@@ -1304,14 +1359,44 @@
end = itemlist.end();
for(; cur != end; cur++) {
ServerLootItem_Struct* sitem = *cur;
- const Item_Struct* item = database.GetItem(sitem->item_id);
- if (item)
- to->Message(0, " %d: %s", item->ID, item->Name);
- else
- to->Message(0, " Error: 0x%04x", sitem->item_id);
- x++;
+
+ if (IsPlayerCorpse()) {
+ if (sitem->equipSlot >= 251 && sitem->equipSlot <= 340)
+ sitem->lootslot = 0xFFFF;
+ else
+ x < 30 ? sitem->lootslot = x : sitem->lootslot = 0xFFFF; // this con value needs to reflect corpselootlimit in MakeLootRequestPackets
+
+ const Item_Struct* item = database.GetItem(sitem->item_id);
+
+ if (item)
+ to->Message((sitem->lootslot == 0xFFFF), " LootSlot: %i (EquipSlot: %i) Item: %s (%d) with %i %s", sitem->lootslot, sitem->equipSlot, item->Name, item->ID, sitem->charges, sitem->charges==1?"charge":"charges");
+ else
+ to->Message((sitem->lootslot == 0xFFFF), " Error: 0x%04x", sitem->item_id);
+
+ if (sitem->lootslot != 0xFFFF)
+ x++;
+
+ y++;
+ }
+ else {
+ sitem->lootslot=y;
+ const Item_Struct* item = database.GetItem(sitem->item_id);
+
+ if (item)
+ to->Message(0, " LootSlot: %i Item: %s (%d) with %i %s", sitem->lootslot, item->Name, item->ID, sitem->charges, sitem->charges==1?"charge":"charges");
+ else
+ to->Message(0, " Error: 0x%04x", sitem->item_id);
+
+ y++;
+ }
}
- to->Message(0, "%i items on %s.", x, this->GetName());
+
+ if (IsPlayerCorpse()) {
+ to->Message(0, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetDBID());
+ }
+ else {
+ to->Message(0, "%i %s on %s.", y, y==1?"item":"items", this->GetName());
+ }
}
bool Corpse::Summon(Client* client, bool spell, bool CheckDistance)
Index: PlayerCorpse.h
===================================================================
--- PlayerCorpse.h (revision 2185)
+++ PlayerCorpse.h (working copy)
@@ -111,7 +111,7 @@
inline int GetRezzExp() { return rezzexp; }
protected:
- void MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot);
+ std::list<uint32> MoveItemToCorpse(Client *client, ItemInst *item, sint16 equipslot);
private:
bool p_PlayerCorpse;
No more updates to this portion of code unless I, or someone else, finds a major flaw in it.
I can post test procedures if anyone needs specific criteria.
__________________
Uleat of Bertoxxulous
Compilin' Dirty
|
|
|
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -4. The time now is 04:20 AM.
|
|
|
|
|
|
|
|
|
|
|
|
|