View Single Post
  #1  
Old 05-07-2004, 09:54 PM
bUsh
Sarnak
 
Join Date: Apr 2004
Location: Waukesha, WI
Posts: 93
Default FIX UPDATE: Loot Related Exploits (Rec. Lvl & 2Handers)

Greetings!

Please Read Post Below for Important Line Change

You may want to call me the 'Nerf Master' after this one, but here it is:

These following changes will prevent players from auto-equipping items they loot if they do not now ALSO meet the Required Level.
As well, the secondary slot must now be empty for a player to auto-equip a 2-Hander from a corpse. This does not affect looting much, items which cannot be equipped now will move to your inventory.

The second change will validate characters logging in \ zoning, and will delete all item configurations deemed 'impossible' (basically, right now it just deletes any items equipped before Req. Level or 2Hand + Secondary configurations).

As always, these changes are implemented on The Green Leaf server-build (which will be available open-source, once I get a site up). If you run into build errors, you may want to check my other posts to see if there are any pre-req changes you need to make.

common\database.h - around line 434 (after ConvertItemBlob) add:
Code:
bool Database::ValidateInventory( uint32 char_id, PlayerProfile_Struct* pp, Inventory* inv )
{
	if( !pp || !inv ) // null?
		return false;

	bool valid = true;

    ItemInst *item = 0;
	for( int i = 0; i < 22; ++i ) // Worn slots
	{
		item = inv->GetItem(i);
		if( item == 0 )
			continue;

		if( item->IsType( ItemTypeCommon ) )
		{
			// Make sure no-one has equipped items their Race, Class, or Level cannot use.
			if( !item->IsEquipable( pp->race, pp->class_, pp->level ) ) // if item shouldn't be equipped
			{
				inv->DeleteItem( i ); // Delete from inventory
				item = 0; // Null the item
				SaveInventory( char_id, item, i ); // Save change
				valid = false; // Inventory was invalid, flag it
			}
			// Make sure no-one has equipped 2 handed weapons & a secondary item
			else if( i == SLOT_PRIMARY && item->IsWeapon() )
			{
				if( (item->GetItem()->Common.Skill == 1) || (item->GetItem()->Common.Skill == 4) || (item->GetItem()->Common.Skill == 35) ) // 2 handed weapons
				{
					if( inv->GetItem(SLOT_SECONDARY) ) // and if secondary slot is not empty
					{
						inv->DeleteItem( i ); // Delete from inventory
						item = 0; // Null the item
						SaveInventory( char_id, item, i ); // Save change
						valid = false; // Inventory was invalid, flag it
					}
				}
			}
		}
	}

	return valid;
}
common\database.cpp - around line 2231 (bottom of GetCharacterInfoForLogin_result) change to this:
Code:
		if (guilddbid)
			*guilddbid = atoi(row[7]);
		if (guildrank)
			*guildrank = atoi(row[8]);
		
// bUsh - ValidateInventory
		// Retrieve character inventory
		bool getInv = GetInventory(char_id, inv);
		if ( !ValidateInventory( char_id, pp, inv ) )
		{
			LogFile->write(EQEMuLog::Status, "Player inventory has been validated");
		}

		return getInv;
// bUsh - ValidateInventory End
	}
	
	return false;
}
common\item.h - around line 310, change virtual IsEquipable arg list:
Code:
virtual bool IsEquipable(int16 race, int16 class_, uint8 level) const;
as well as around line 383:
Code:
virtual bool IsEquipable(int16 race, int16 class_, uint8 level) const;
common\item.cpp - around line 178, change function arg list:
Code:
bool ItemInst::IsEquipable(int16, int16, uint8) const
and around line 208, change entire IsEquipable function to:
Code:
// Can item be equipped?
bool ItemCommonInst::IsEquipable(int16 race, int16 class_, uint8 level) const
{
	if (!m_item)
		return false;
	
	bool israce = false;
	bool isclass = false;
	bool islevel = false;
	
	if (m_item->EquipSlots == 0) {
		return false;
	}
	
	uint32 classes_ = m_item->Common.Classes;
	uint32 races_ = m_item->Common.Races;
	uint8 level_ = m_item->Common.RequiredLevel;
	
	// @merth: can this be optimized?  i.e., will (race & common->Races) suffice?
	for (int cur_class = 1; cur_class<=15; cur_class++) {
		if (classes_ % 2 == 1) {
    		if (cur_class == class_) {
    			isclass = true;
			}
		}
		classes_ >>= 1;
	}
	for (int cur_race = 1; cur_race <= 14; cur_race++) {
		if (races_ %2 == 1) {
    		if (cur_race == race) {
    			israce = true;
   			}
		}
		races_ >>= 1;
	}

	if( level_ <= level )
		islevel = true;
	
	return (israce && isclass && islevel);
}
Finally:

zone\client.cpp - around line 2350 (autoputlootininventory), change entire first if block (auto equip) to:
Code:
	// #1: Try to auto equip
	if (try_worn && inst.IsEquipable(GetRace(), GetClass(), GetLevel()))
	{
		for (sint16 i = 0; i < 22; i++)
		{
			if (!m_inv[i])
			{
				if( i == SLOT_PRIMARY && inst.IsWeapon() ) // If item is primary slot weapon
				{
					if( (inst.GetItem()->Common.Skill == 1) || (inst.GetItem()->Common.Skill == 4) || (inst.GetItem()->Common.Skill == 35) ) // and uses 2hs \ 2hb \ 2hp
					{
						if( m_inv[SLOT_SECONDARY] ) // and if secondary slot is not empty
						{
							continue; // Can't auto-equip
						}
					}
				}
                
				if
				(
					i == SLOT_SECONDARY &&
					inst.IsWeapon() &&
					!CanThisClassDualWield()
				)
				{
					continue;
				}

				if (inst.IsEquipable(i))	// Equippable at this slot?
				{
					PutLootInInventory(i, inst);
					return true;
				}
			}
		}
	}
You're done! Tell me if I missed anything.

ValidateInventory can be modified to include any new checks for item errors etc.

Enjoy - this made a lot of sploiters on my server angry, Oh well!
__________________
~ b [ u ] s h

* ServerOP * The Green Leaf Server
Reply With Quote