Browse Source

Lacertae Update #4

Fix #376 - resolved spells with duration until cancel blinking after zoning
Fix #375 - hard coded activity status to solid/transport flags on lifts/boats resolves falling through lifts when moving on them

Additional crash fixes:
- stats clearing when calculating bonuses on entity
- Player equipment mutex lock protection
Image 2 years ago
parent
commit
b93cabc72e

+ 3 - 7
EQ2/source/WorldServer/Entity.cpp

@@ -793,9 +793,6 @@ void Entity::DoRegenUpdate(){
 			SetHP(GetTotalHP());
 		else
 			SetHP(hp + temp);
-
-		LogWrite(MISC__TODO, 1, "TODO", "Fix this later for mobs\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
-
 	}
 	if(GetPower() < GetTotalPower()){
 		sint16 temp = GetInfoStruct()->get_power_regen();
@@ -804,8 +801,6 @@ void Entity::DoRegenUpdate(){
 			SetPower(GetTotalPower());
 		else
 			SetPower(power + temp);
-
-		LogWrite(MISC__TODO, 1, "TODO", "Fix this later for mobs\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
 	}
 }
 
@@ -1129,8 +1124,6 @@ void Entity::CalculateBonuses(){
 	info->set_cur_mitigation(info->get_mitigation_base());
 	info->set_base_avoidance_pct(info->get_avoidance_base());
 
-	LogWrite(MISC__TODO, 1, "TODO", "Calculate via current spells\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
-
 	info->set_disease(info->get_disease_base());
 	info->set_divine(info->get_divine_base());
 	info->set_heat(info->get_heat_base());
@@ -1174,7 +1167,10 @@ void Entity::CalculateBonuses(){
 
 	info->set_offensivespeed(0);
 
+	MStats.lock();
 	stats.clear();
+	MStats.unlock();
+
 	ItemStatsValues* values = equipment_list.CalculateEquipmentBonuses(this);
 	CalculateSpellBonuses(values);
 	info->add_sta((float)values->sta);

+ 1 - 1
EQ2/source/WorldServer/Items/Items.h

@@ -1101,6 +1101,7 @@ public:
 	EquipmentItemList(const EquipmentItemList& list);
 	~EquipmentItemList();
 	Item* items[NUM_SLOTS];
+	Mutex MEquipmentItems;
 
 	vector<Item*>* GetAllEquippedItems();
 
@@ -1127,7 +1128,6 @@ public:
 	void	SetAppearanceType(int8 type) { AppearanceType = type; }
 	int8	GetAppearanceType() { return AppearanceType; }
 private:
-	Mutex MEquipmentItems;
 	int8 AppearanceType; // 0 for normal equip, 1 for appearance
 };
 

+ 23 - 2
EQ2/source/WorldServer/Player.cpp

@@ -1712,6 +1712,7 @@ bool Player::DamageEquippedItems(int8 amount, Client* client) {
 	bool ret = false;
 	int8 item_type;
 	Item* item = 0;
+	equipment_list.MEquipmentItems.readlock(__FUNCTION__, __LINE__);
 	for(int8 i=0;i<NUM_SLOTS;i++){
 		item = equipment_list.items[i];
 		if(item) {
@@ -1729,6 +1730,8 @@ bool Player::DamageEquippedItems(int8 amount, Client* client) {
 			}
 		}
 	}
+	equipment_list.MEquipmentItems.releasereadlock(__FUNCTION__, __LINE__);
+
 	return ret;
 }
 
@@ -1783,8 +1786,10 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 		LogWrite(PLAYER__ERROR, 0, "Player", "%u index is out of range for equip items, bag_id: %i, slot: %u, version: %u, appearance: %u", index, bag_id, slot, version, appearance_type);
 		return packets;
 	}
-
+	equipList->MEquipmentItems.readlock(__FUNCTION__, __LINE__);
 	Item* item = equipList->items[index];
+	equipList->MEquipmentItems.releasereadlock(__FUNCTION__, __LINE__);
+
 	if (item && bag_id == -999) {
 		int8 old_slot = item->details.slot_id;
 		if (item_list.AssignItemToFreeSlot(item)) {
@@ -1836,9 +1841,17 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 				bag_id = 0;
 		}
 
+		item_list.MPlayerItems.readlock(__FUNCTION__, __LINE__);
 		if (item_list.items.count(bag_id) > 0 && item_list.items[bag_id][BASE_EQUIPMENT].count(slot) > 0)
 			to_item = item_list.items[bag_id][BASE_EQUIPMENT][slot];
+
+		bool canEquipToSlot = false;
 		if (to_item && equipList->CanItemBeEquippedInSlot(to_item, ConvertSlotFromClient(item->details.slot_id, version))) {
+			canEquipToSlot = true;
+		}
+		item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
+
+		if (canEquipToSlot) {
 			equipList->RemoveItem(index);
 			if(item->details.appearance_type)
 				database.DeleteItem(GetCharacterID(), item, "APPEARANCE");
@@ -1877,7 +1890,10 @@ vector<EQ2Packet*>	Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
 		else if (to_item && to_item->IsBag() && to_item->details.num_slots > 0) {
 			bool free_slot = false;
 			for (int8 i = 0; i < to_item->details.num_slots; i++) {
-				if (item_list.items[to_item->details.bag_id][appearance_type].count(i) == 0) {
+				item_list.MPlayerItems.readlock(__FUNCTION__, __LINE__);
+				int32 count = item_list.items[to_item->details.bag_id][appearance_type].count(i);
+				item_list.MPlayerItems.releasereadlock(__FUNCTION__, __LINE__);
+				if (count == 0) {
 					SetEquipment(0, item->details.slot_id);
 
 					if(item->details.appearance_type)
@@ -2029,14 +2045,19 @@ EQ2Packet* Player::SwapEquippedItems(int8 slot1, int8 slot2, int16 version, int1
 	if(equip_type == 3)
 		equipList = &appearance_equipment_list;
 	
+	equipList->MEquipmentItems.readlock(__FUNCTION__, __LINE__);
 	Item* item_from = equipList->items[slot1];
 	Item* item_to = equipList->items[slot2];
+	equipList->MEquipmentItems.releasereadlock(__FUNCTION__, __LINE__);
+
 	if(item_from && equipList->CanItemBeEquippedInSlot(item_from, slot2)){
 		if(item_to){
 			if(!equipList->CanItemBeEquippedInSlot(item_to, slot1))
 				return 0;
 		}
+		equipList->MEquipmentItems.writelock(__FUNCTION__, __LINE__);
 		equipList->items[slot1] = nullptr;
+		equipList->MEquipmentItems.releasewritelock(__FUNCTION__, __LINE__);
 		equipList->SetItem(slot2, item_from);
 		if(item_to)
 		{

+ 3 - 0
EQ2/source/WorldServer/Spawn.cpp

@@ -2563,6 +2563,9 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		// doesn't work for DoF client version 546
 		if (appearance.icon == 28 || appearance.icon == 12 || IsTransportSpawn())
 		{
+			// there is some other flags that setting with a transport breaks their solidity/ability to properly transport
+			// thus we just consider the following flags for now as all necessary
+			temp_activity_status = ACTIVITY_STATUS_SOLID_1188;
 			temp_activity_status += ACTIVITY_STATUS_ISTRANSPORT_1188;
 		}
 	}

+ 4 - 1
EQ2/source/WorldServer/World.cpp

@@ -1679,8 +1679,11 @@ void World::AddBonuses(Item* item, ItemStatsValues* values, int16 type, sint32 v
 				break;
 			}*/
 			default: {
-				if (entity)
+				if (entity) {
+					entity->MStats.lock();
 					entity->stats[type] += value;
+					entity->MStats.unlock();
+				}
 				break;
 					 }
 		}

+ 8 - 3
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -7651,8 +7651,10 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
 					safe_delete(lua_spell);
 				continue;
 			}
-			
-			info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
+			if(spell->GetSpellData()->duration_until_cancel)
+				info->spell_effects[effect_slot].expire_timestamp = 0xFFFFFFFF;
+			else
+				info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
 			info->spell_effects[effect_slot].icon = icon;
 			info->spell_effects[effect_slot].icon_backdrop = icon_backdrop;
 			info->spell_effects[effect_slot].spell_id = spell_id;
@@ -7771,7 +7773,10 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
 			info->maintained_effects[effect_slot].slot_pos = slot_pos;
 			info->maintained_effects[effect_slot].target = targetID;
 			info->maintained_effects[effect_slot].target_type = target_type;
-			info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
+			if(spell->GetSpellData()->duration_until_cancel)
+				info->maintained_effects[effect_slot].expire_timestamp = 0xFFFFFFFF;
+			else
+				info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
 			info->maintained_effects[effect_slot].icon = icon;
 			info->maintained_effects[effect_slot].icon_backdrop = icon_backdrop;
 			info->maintained_effects[effect_slot].spell_id = spell_id;