diff --git a/zone/client.cpp b/zone/client.cpp index f823c8b54..9968dcb4e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7041,7 +7041,7 @@ void Client::UpdateClientXTarget(Client *c) } } -void Client::AddAutoXTarget(Mob *m) +void Client::AddAutoXTarget(Mob *m, bool send) { if(!XTargettingAvailable() || !XTargetAutoAddHaters) return; @@ -7054,7 +7054,10 @@ void Client::AddAutoXTarget(Mob *m) if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0)) { XTargets[i].ID = m->GetID(); - SendXTargetPacket(i, m); + if (send) // if we don't send we're bulk sending updates later on + SendXTargetPacket(i, m); + else + XTargets[i].dirty = true; break; } } @@ -7062,42 +7065,59 @@ void Client::AddAutoXTarget(Mob *m) void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) { - if(!XTargettingAvailable()) + if (!XTargettingAvailable()) return; bool HadFreeAutoSlotsBefore = false; int FreedAutoSlots = 0; - if(m->GetID() == 0) + if (m->GetID() == 0) return; - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(OnlyAutoSlots && (XTargets[i].Type !=Auto)) + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (OnlyAutoSlots && XTargets[i].Type != Auto) continue; - if(XTargets[i].ID == m->GetID()) - { - if(XTargets[i].Type == CurrentTargetNPC) + if (XTargets[i].ID == m->GetID()) { + if (XTargets[i].Type == CurrentTargetNPC) XTargets[i].Type = Auto; - if(XTargets[i].Type == Auto) + if (XTargets[i].Type == Auto) ++FreedAutoSlots; XTargets[i].ID = 0; - - SendXTargetPacket(i, nullptr); - } - else - { - if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0)) + XTargets[i].dirty = true; + } else { + if (XTargets[i].Type == Auto && XTargets[i].ID == 0) HadFreeAutoSlotsBefore = true; } } - // If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we just freed up. - if(!HadFreeAutoSlotsBefore && FreedAutoSlots) + + // move shit up! + std::queue empty_slots; + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (XTargets[i].Type != Auto) + continue; + + if (XTargets[i].ID == 0) { + empty_slots.push(i); + continue; + } + + if (XTargets[i].ID != 0 && !empty_slots.empty()) { + int temp = empty_slots.front(); + std::swap(XTargets[i], XTargets[temp]); + XTargets[i].dirty = XTargets[temp].dirty = true; + empty_slots.pop(); + empty_slots.push(i); + } + } + // If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we + // just freed up. + if (!HadFreeAutoSlotsBefore && FreedAutoSlots) entity_list.RefreshAutoXTargets(this); + SendXTargetUpdates(); } void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) @@ -7166,6 +7186,42 @@ void Client::SendXTargetPacket(uint32 Slot, Mob *m) FastQueuePacket(&outapp); } +// This is a bulk packet, we use it when we remove something since we need to reorder the xtargets and maybe +// add new mobs! Currently doesn't check if there is a dirty flag set, so it should only be called when there is +void Client::SendXTargetUpdates() +{ + if (!XTargettingAvailable()) + return; + + int count = 0; + // header is 4 bytes max xtargets, 4 bytes count + // entry is 4 bytes slot, 1 byte unknown, 4 bytes ID, 65 char name + auto outapp = new EQApplicationPacket(OP_XTargetResponse, 8 + 74 * GetMaxXTargets()); // fuck it max size + outapp->WriteUInt32(GetMaxXTargets()); + outapp->WriteUInt32(1); // we will correct this later + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (XTargets[i].dirty) { + outapp->WriteUInt32(i); + outapp->WriteUInt8(0); // no idea what this is + outapp->WriteUInt32(XTargets[i].ID); + outapp->WriteString(XTargets[i].Name); + count++; + XTargets[i].dirty = false; + } + } + + assert(count > 0); // we don't have any logic to prevent this, assert for now + + auto newbuff = new uchar[outapp->GetWritePosition()]; + memcpy(newbuff, outapp->pBuffer, outapp->GetWritePosition()); + safe_delete_array(outapp->pBuffer); + outapp->pBuffer = newbuff; + outapp->size = outapp->GetWritePosition(); + outapp->SetWritePosition(4); + outapp->WriteUInt32(count); + FastQueuePacket(&outapp); +} + void Client::RemoveGroupXTargets() { if(!XTargettingAvailable()) @@ -8670,4 +8726,4 @@ uint32 Client::GetMoney(uint8 type, uint8 subtype) { int Client::GetAccountAge() { return (time(nullptr) - GetAccountCreation()); -} \ No newline at end of file +} diff --git a/zone/client.h b/zone/client.h index 4b72ee94e..9fa68350e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -175,6 +175,7 @@ typedef enum struct XTarget_Struct { XTargetType Type; + bool dirty; uint16 ID; char Name[65]; }; @@ -1144,9 +1145,10 @@ public: bool IsClientXTarget(const Client *c) const; void UpdateClientXTarget(Client *c); void UpdateXTargetType(XTargetType Type, Mob *m, const char *Name = nullptr); - void AddAutoXTarget(Mob *m); + void AddAutoXTarget(Mob *m, bool send = true); void RemoveXTarget(Mob *m, bool OnlyAutoSlots); void SendXTargetPacket(uint32 Slot, Mob *m); + void SendXTargetUpdates(); void RemoveGroupXTargets(); void RemoveAutoXTargets(); void ShowXTargets(Client *c); diff --git a/zone/entity.cpp b/zone/entity.cpp index 01f27700e..69b13e6eb 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1373,7 +1373,7 @@ void EntityList::RefreshAutoXTargets(Client *c) continue; if (m->CheckAggro(c) && !c->IsXTarget(m)) { - c->AddAutoXTarget(m); + c->AddAutoXTarget(m, false); // we only call this before a bulk, so lets not send right away break; }