finished the implementation

This commit is contained in:
dannuic 2026-04-27 19:13:20 -06:00
parent d16c1b2483
commit c384ef6215
9 changed files with 33 additions and 176 deletions

View File

@ -68,8 +68,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_BlockedBuffs` | 🟢 Verified | | |
| `OP_BoardBoat` | 🟡 Unverified | | |
| `OP_BookButton` | 🟡 Unverified | | |
| `OP_BuffDefinition` | 🟡 Unverified | | |
| `OP_RefreshBuffs` | 🟡 Unverified | | |
| `OP_BuffDefinition` | 🟢 Verified | | |
| `OP_BuffRemoveRequest` | 🟡 Unverified | | |
| `OP_Bug` | 🟡 Unverified | | |
| `OP_BuyerItems` | 🔴 Not-Set | | |
@ -395,7 +394,6 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_OpenInventory` | 🔴 Not-Set | | |
| `OP_OpenTributeMaster` | 🔴 Not-Set | | |
| `OP_PDeletePetition` | 🔴 Not-Set | | |
| `OP_RefreshPetBuffs` | 🔴 Not-Set | | |
| `OP_PetCommands` | 🔴 Not-Set | | |
| `OP_PetCommandState` | 🔴 Not-Set | | |
| `OP_PetHoTT` | 🔴 Not-Set | | |
@ -451,6 +449,9 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_RecipesFavorite` | 🟡 Unverified | | |
| `OP_RecipesSearch` | 🟡 Unverified | | |
| `OP_ReclaimCrystals` | 🔴 Not-Set | | |
| `OP_RefreshBuffs` | 🟢 Verified | | |
| `OP_RefreshPetBuffs` | 🟢 Verified | | |
| `OP_RefreshTargetBuffs` | 🟢 Verified | | |
| `OP_ReloadUI` | 🔴 Not-Set | | |
| `OP_RemoveAllDoors` | 🟡 Unverified | | |
| `OP_RemoveBlockedBuffs` | 🟢 Verified | | |
@ -551,7 +552,6 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_Surname` | 🔴 Not-Set | | |
| `OP_SwapSpell` | 🟢 Verified | | |
| `OP_SystemFingerprint` | 🔴 Not-Set | | |
| `OP_RefreshTargetBuffs` | 🔴 Not-Set | | |
| `OP_TargetCommand` | 🟡 Unverified | | |
| `OP_TargetHoTT` | 🔴 Not-Set | | |
| `OP_TargetMouse` | 🟡 Unverified | | |

View File

@ -6326,8 +6326,7 @@ void Client::SuspendMinion(int value)
if(value >= 1)
{
CurrentPet->SetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items);
CurrentPet->SendPetBuffsToClient();
Buff::SendFullBuffRefresh(CurrentPet);
}
CurrentPet->CalcBonuses();

View File

@ -1268,11 +1268,6 @@ public:
const std::string GetAutoLoginCharacterName();
bool SetAutoLoginCharacterName(const std::string& character_name);
//This is used to later set the buff duration of the spell, in slot to duration.
//Doesn't appear to work directly after the client recieves an action packet.
void SendBuffDurationPacket(Buffs_Struct &buff, int slot);
void SendBuffNumHitPacket(Buffs_Struct &buff, int slot);
void ProcessInspectRequest(Client* requestee, Client* requester);
bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); }
int FindSpellBookSlotBySpellID(int32 spellid);
@ -1956,7 +1951,6 @@ public:
protected:
friend class Mob;
void CalcEdibleBonuses(StatBonuses* newbon);
void MakeBuffFadePacket(int32 spell_id, int slot_id, bool send_message = true);
bool client_data_loaded;

View File

@ -760,7 +760,7 @@ void Client::CompleteConnect()
Mob* pet = GetPet();
if (pet) {
pet->SendWearChangeAndLighting(EQ::textures::LastTexture);
pet->SendPetBuffsToClient();
Buff::SendFullBuffRefresh(pet);
}
if (GetGroup())

View File

@ -295,6 +295,18 @@ inline void SendSingleBuffChange(Mob* sender, const Buffs_Struct& buff, int slot
ClientPatch::QueueClientsByTarget(sender, ackreq, false, ShouldSendTargetBuffs, mutate)(
&IBuff::RefreshBuffs, GetComponent, OP_RefreshTargetBuffs, sender, remove, suspended, slots);
// the client doesn't automatically do this for some reason (RoF2? I think this is in TOB)
// TODO: hook this up to QueueClients, or figure out if there's another missing packet to display the fade text
if (remove && sender->IsClient())
{
const char *fadetext = spells[buff.spellid].spell_fades;
auto outapp = std::make_unique<EQApplicationPacket>(OP_ColoredText, sizeof(ColoredText_Struct) + strlen(fadetext));
ColoredText_Struct *bfm = (ColoredText_Struct *) outapp->pBuffer;
bfm->color = Chat::Spells;
memcpy(bfm->msg, fadetext, strlen(fadetext));
sender->CastToClient()->QueuePacket(outapp.get());
}
}
} // namespace Buff

View File

@ -242,7 +242,7 @@ struct Buffs_Struct {
int32 virus_spread_time; //time till next attempted viral spread
bool persistant_buff;
bool client; //True if the caster is a client
bool UpdateClient;
bool UpdateClient; // This is for legacy client support only. Newer clients take refresh packets for the entire buff list
// cereal
template<class Archive>

View File

@ -459,7 +459,6 @@ public:
int AddBuff(Mob *caster, const int32 spell_id, int duration = 0, int32 level_override = -1, bool disable_buff_overwrite = false);
int CanBuffStack(int32 spellid, uint8 caster_level, bool iFailIfOverwrite = false);
int CalcBuffDuration(Mob *caster, Mob *target, int32 spell_id, int32 caster_level_override = -1);
void SendPetBuffsToClient();
virtual int GetCurrentBuffSlots() const { return 0; }
virtual int GetCurrentSongSlots() const { return 0; }
virtual int GetCurrentDiscSlots() const { return 0; }

View File

@ -821,7 +821,7 @@ bool Mob::SpellEffect(Mob* caster, int32 spell_id, float partial, int level_over
ps->command = 1;
entity_list.QueueClients(this, app);
safe_delete(app);
SendPetBuffsToClient();
Buff::SendFullBuffRefresh(this);
SendAppearancePacket(AppearanceType::Pet, caster->GetID(), true, true);
}
@ -3864,19 +3864,15 @@ void Mob::BuffProcess()
}
}
if(IsClient())
{
if(buffs[buffs_i].UpdateClient == true)
{
CastToClient()->SendBuffDurationPacket(buffs[buffs_i], buffs_i);
// Hack to get UF to play nicer, RoF seems fine without it
if (CastToClient()->ClientVersion() == EQ::versions::ClientVersion::UF && buffs[buffs_i].hit_number > 0)
CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i);
buffs[buffs_i].UpdateClient = false;
}
// this is for older clients. Newer clients will simply discard this packet
if (IsClient() && buffs[buffs_i].UpdateClient == true) {
Buff::SendSingleBuffChange(this, buffs[buffs_i], buffs_i);
buffs[buffs_i].UpdateClient = false;
}
}
}
Buff::SendFullBuffRefresh(this);
}
void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
@ -4248,7 +4244,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
return;
if (IsClient() && !CastToClient()->IsDead())
CastToClient()->MakeBuffFadePacket(buffs[slot].spellid, slot);
Buff::SendSingleBuffChange(this, buffs[slot], slot);
LogSpells("Fading buff [{}] from slot [{}]", buffs[slot].spellid, slot);
@ -7014,7 +7010,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, int32 spell_id)
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
Buff::SendSingleBuffChange(this, buffs[d], d);
}
}
}
@ -7029,7 +7025,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, int32 spell_id)
if (!TryFadeEffect(buff_slot))
BuffFadeBySlot(buff_slot , true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[buff_slot], buff_slot);
Buff::SendSingleBuffChange(this, buffs[buff_slot], buff_slot);
}
}
}
@ -7047,7 +7043,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, int32 spell_id)
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
Buff::SendSingleBuffChange(this, buffs[d], d);
}
}
}

View File

@ -2856,7 +2856,7 @@ bool Mob::SpellFinished(int32 spell_id, Mob *spell_target, CastingSlot slot, int
if (IsClient() && IsEffectInSpell(spell_id, SpellEffect::BindSight)) {
for (int i = 0; i < GetMaxTotalSlots(); i++) {
if (buffs[i].spellid == spell_id) {
CastToClient()->SendBuffNumHitPacket(buffs[i], i);//its hack, it works.
Buff::SendSingleBuffChange(this, buffs[i], i);//its hack, it works.
}
}
}
@ -2864,7 +2864,7 @@ bool Mob::SpellFinished(int32 spell_id, Mob *spell_target, CastingSlot slot, int
if (IsClient() && spells[spell_id].hit_number) {
for (int i = 0; i < GetMaxTotalSlots(); i++) {
if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) {
CastToClient()->SendBuffNumHitPacket(buffs[i], i);
Buff::SendSingleBuffChange(this, buffs[i], i);
break;
}
}
@ -5809,53 +5809,6 @@ void Mob::Mesmerize()
StopNavigation();
}
void Client::MakeBuffFadePacket(int32 spell_id, int slot_id, bool send_message)
{
EQApplicationPacket* outapp = nullptr;
outapp = new EQApplicationPacket(OP_BuffDefinition, sizeof(SpellBuffPacket_Struct));
SpellBuffPacket_Struct* sbf = (SpellBuffPacket_Struct*) outapp->pBuffer;
sbf->entityid = GetID();
// i dont know why but this works.. for now
sbf->buff.effect_type = 2;
// sbf->slot=m_pp.buffs[slot_id].slotid;
// sbf->level=m_pp.buffs[slot_id].level;
// sbf->effect=m_pp.buffs[slot_id].effect;
sbf->buff.spellid = spell_id;
sbf->slotid = slot_id;
sbf->bufffade = 1;
#if EQDEBUG >= 11
printf("Sending SBF 1 from server:\n");
DumpPacket(outapp);
#endif
QueuePacket(outapp);
/*
sbf->effect=0;
sbf->level=0;
sbf->slot=0;
*/
sbf->buff.spellid = 0xffffffff;
#if EQDEBUG >= 11
printf("Sending SBF 2 from server:\n");
DumpPacket(outapp);
#endif
QueuePacket(outapp);
safe_delete(outapp);
if(send_message)
{
const char *fadetext = spells[spell_id].spell_fades;
outapp = new EQApplicationPacket(OP_ColoredText, sizeof(ColoredText_Struct) + strlen(fadetext));
ColoredText_Struct *bfm = (ColoredText_Struct *) outapp->pBuffer;
bfm->color = Chat::Spells;
memcpy(bfm->msg, fadetext, strlen(fadetext));
QueuePacket(outapp);
safe_delete(outapp);
}
}
void Client::MemSpell(int32 spell_id, int slot, bool update_client)
{
if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) {
@ -6501,99 +6454,6 @@ int Mob::GetCasterLevel(int32 spell_id) {
return std::max(1, level);
}
//This member function sets the buff duration on the client
//however it does not work if sent quickly after an action packets, which is what one might perfer to do
//Thus I use this in the buff process to update the correct duration once after casting
//this allows AAs and focus effects that increase buff duration to work correctly, but could probably
//be used for other things as well
void Client::SendBuffDurationPacket(Buffs_Struct &buff, int slot)
{
EQApplicationPacket* outapp = nullptr;
outapp = new EQApplicationPacket(OP_BuffDefinition, sizeof(SpellBuffPacket_Struct));
SpellBuffPacket_Struct* sbf = (SpellBuffPacket_Struct*) outapp->pBuffer;
sbf->entityid = GetID();
sbf->buff.effect_type = 2;
sbf->buff.level = buff.casterlevel > 0 ? buff.casterlevel : GetLevel();
sbf->buff.bard_modifier = buff.instrument_mod;
sbf->buff.spellid = buff.spellid;
sbf->buff.duration = buff.ticsremaining;
if (buff.dot_rune)
sbf->buff.counters = buff.dot_rune;
else if (buff.magic_rune)
sbf->buff.counters = buff.magic_rune;
else if (buff.melee_rune)
sbf->buff.counters = buff.melee_rune;
else if (buff.counters)
sbf->buff.counters = buff.counters;
sbf->buff.player_id = buff.casterid;
sbf->buff.num_hits = buff.hit_number;
sbf->buff.y = buff.caston_y;
sbf->buff.x = buff.caston_x;
sbf->buff.z = buff.caston_z;
sbf->slotid = slot;
sbf->bufffade = 0;
FastQueuePacket(&outapp);
}
void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot)
{
// UF+ use this packet
if (ClientVersion() < EQ::versions::ClientVersion::UF)
return;
EQApplicationPacket *outapp = nullptr;
outapp = new EQApplicationPacket(OP_RefreshBuffs, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct));
BuffIcon_Struct *bi = (BuffIcon_Struct *)outapp->pBuffer;
bi->entity_id = GetID();
bi->count = 1;
bi->all_buffs = 0;
bi->tic_timer = tic_timer.GetRemainingTime();
bi->entries[0].buff_slot = slot;
bi->entries[0].spell_id = buff.spellid;
bi->entries[0].tics_remaining = buff.ticsremaining;
bi->entries[0].num_hits = buff.hit_number;
strn0cpy(bi->entries[0].caster, buff.caster_name, 64);
bi->name_lengths = strlen(bi->entries[0].caster);
FastQueuePacket(&outapp);
}
void Mob::SendPetBuffsToClient()
{
// Don't really need this check, as it should be checked before this method is called, but it doesn't hurt
// too much to check again.
if(!(GetOwner() && GetOwner()->IsClient()))
return;
int PetBuffCount = 0;
auto outapp = new EQApplicationPacket(OP_RefreshPetBuffs, sizeof(PetBuff_Struct));
PetBuff_Struct* pbs=(PetBuff_Struct*)outapp->pBuffer;
memset(outapp->pBuffer,0,outapp->size);
pbs->petid=GetID();
int MaxSlots = GetMaxTotalSlots();
if(MaxSlots > PET_BUFF_COUNT)
MaxSlots = PET_BUFF_COUNT;
for(int buffslot = 0; buffslot < MaxSlots; buffslot++)
{
if (IsValidSpell(buffs[buffslot].spellid)) {
pbs->spellid[buffslot] = buffs[buffslot].spellid;
pbs->ticsremaining[buffslot] = buffs[buffslot].ticsremaining;
PetBuffCount++;
}
}
pbs->buffcount=PetBuffCount;
GetOwner()->CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
}
void Mob::BuffModifyDurationBySpellID(int32 spell_id, int32 newDuration)
{
int buff_count = GetMaxTotalSlots();
@ -6602,10 +6462,7 @@ void Mob::BuffModifyDurationBySpellID(int32 spell_id, int32 newDuration)
if (buffs[i].spellid == spell_id)
{
buffs[i].ticsremaining = newDuration;
if(IsClient())
{
CastToClient()->SendBuffDurationPacket(buffs[i], i);
}
Buff::SendSingleBuffChange(this, buffs[i], i);
}
}
}