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
+4 -4
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_BlockedBuffs` | 🟢 Verified | | |
| `OP_BoardBoat` | 🟡 Unverified | | | | `OP_BoardBoat` | 🟡 Unverified | | |
| `OP_BookButton` | 🟡 Unverified | | | | `OP_BookButton` | 🟡 Unverified | | |
| `OP_BuffDefinition` | 🟡 Unverified | | | | `OP_BuffDefinition` | 🟢 Verified | | |
| `OP_RefreshBuffs` | 🟡 Unverified | | |
| `OP_BuffRemoveRequest` | 🟡 Unverified | | | | `OP_BuffRemoveRequest` | 🟡 Unverified | | |
| `OP_Bug` | 🟡 Unverified | | | | `OP_Bug` | 🟡 Unverified | | |
| `OP_BuyerItems` | 🔴 Not-Set | | | | `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_OpenInventory` | 🔴 Not-Set | | |
| `OP_OpenTributeMaster` | 🔴 Not-Set | | | | `OP_OpenTributeMaster` | 🔴 Not-Set | | |
| `OP_PDeletePetition` | 🔴 Not-Set | | | | `OP_PDeletePetition` | 🔴 Not-Set | | |
| `OP_RefreshPetBuffs` | 🔴 Not-Set | | |
| `OP_PetCommands` | 🔴 Not-Set | | | | `OP_PetCommands` | 🔴 Not-Set | | |
| `OP_PetCommandState` | 🔴 Not-Set | | | | `OP_PetCommandState` | 🔴 Not-Set | | |
| `OP_PetHoTT` | 🔴 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_RecipesFavorite` | 🟡 Unverified | | |
| `OP_RecipesSearch` | 🟡 Unverified | | | | `OP_RecipesSearch` | 🟡 Unverified | | |
| `OP_ReclaimCrystals` | 🔴 Not-Set | | | | `OP_ReclaimCrystals` | 🔴 Not-Set | | |
| `OP_RefreshBuffs` | 🟢 Verified | | |
| `OP_RefreshPetBuffs` | 🟢 Verified | | |
| `OP_RefreshTargetBuffs` | 🟢 Verified | | |
| `OP_ReloadUI` | 🔴 Not-Set | | | | `OP_ReloadUI` | 🔴 Not-Set | | |
| `OP_RemoveAllDoors` | 🟡 Unverified | | | | `OP_RemoveAllDoors` | 🟡 Unverified | | |
| `OP_RemoveBlockedBuffs` | 🟢 Verified | | | | `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_Surname` | 🔴 Not-Set | | |
| `OP_SwapSpell` | 🟢 Verified | | | | `OP_SwapSpell` | 🟢 Verified | | |
| `OP_SystemFingerprint` | 🔴 Not-Set | | | | `OP_SystemFingerprint` | 🔴 Not-Set | | |
| `OP_RefreshTargetBuffs` | 🔴 Not-Set | | |
| `OP_TargetCommand` | 🟡 Unverified | | | | `OP_TargetCommand` | 🟡 Unverified | | |
| `OP_TargetHoTT` | 🔴 Not-Set | | | | `OP_TargetHoTT` | 🔴 Not-Set | | |
| `OP_TargetMouse` | 🟡 Unverified | | | | `OP_TargetMouse` | 🟡 Unverified | | |
+1 -2
View File
@@ -6326,8 +6326,7 @@ void Client::SuspendMinion(int value)
if(value >= 1) if(value >= 1)
{ {
CurrentPet->SetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items); CurrentPet->SetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items);
Buff::SendFullBuffRefresh(CurrentPet);
CurrentPet->SendPetBuffsToClient();
} }
CurrentPet->CalcBonuses(); CurrentPet->CalcBonuses();
-6
View File
@@ -1268,11 +1268,6 @@ public:
const std::string GetAutoLoginCharacterName(); const std::string GetAutoLoginCharacterName();
bool SetAutoLoginCharacterName(const std::string& character_name); 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); void ProcessInspectRequest(Client* requestee, Client* requester);
bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); } bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); }
int FindSpellBookSlotBySpellID(int32 spellid); int FindSpellBookSlotBySpellID(int32 spellid);
@@ -1956,7 +1951,6 @@ public:
protected: protected:
friend class Mob; friend class Mob;
void CalcEdibleBonuses(StatBonuses* newbon); void CalcEdibleBonuses(StatBonuses* newbon);
void MakeBuffFadePacket(int32 spell_id, int slot_id, bool send_message = true);
bool client_data_loaded; bool client_data_loaded;
+1 -1
View File
@@ -760,7 +760,7 @@ void Client::CompleteConnect()
Mob* pet = GetPet(); Mob* pet = GetPet();
if (pet) { if (pet) {
pet->SendWearChangeAndLighting(EQ::textures::LastTexture); pet->SendWearChangeAndLighting(EQ::textures::LastTexture);
pet->SendPetBuffsToClient(); Buff::SendFullBuffRefresh(pet);
} }
if (GetGroup()) if (GetGroup())
+12
View File
@@ -295,6 +295,18 @@ inline void SendSingleBuffChange(Mob* sender, const Buffs_Struct& buff, int slot
ClientPatch::QueueClientsByTarget(sender, ackreq, false, ShouldSendTargetBuffs, mutate)( ClientPatch::QueueClientsByTarget(sender, ackreq, false, ShouldSendTargetBuffs, mutate)(
&IBuff::RefreshBuffs, GetComponent, OP_RefreshTargetBuffs, sender, remove, suspended, slots); &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 } // namespace Buff
+1 -1
View File
@@ -242,7 +242,7 @@ struct Buffs_Struct {
int32 virus_spread_time; //time till next attempted viral spread int32 virus_spread_time; //time till next attempted viral spread
bool persistant_buff; bool persistant_buff;
bool client; //True if the caster is a client 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 // cereal
template<class Archive> template<class Archive>
-1
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 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 CanBuffStack(int32 spellid, uint8 caster_level, bool iFailIfOverwrite = false);
int CalcBuffDuration(Mob *caster, Mob *target, int32 spell_id, int32 caster_level_override = -1); int CalcBuffDuration(Mob *caster, Mob *target, int32 spell_id, int32 caster_level_override = -1);
void SendPetBuffsToClient();
virtual int GetCurrentBuffSlots() const { return 0; } virtual int GetCurrentBuffSlots() const { return 0; }
virtual int GetCurrentSongSlots() const { return 0; } virtual int GetCurrentSongSlots() const { return 0; }
virtual int GetCurrentDiscSlots() const { return 0; } virtual int GetCurrentDiscSlots() const { return 0; }
+11 -15
View File
@@ -821,7 +821,7 @@ bool Mob::SpellEffect(Mob* caster, int32 spell_id, float partial, int level_over
ps->command = 1; ps->command = 1;
entity_list.QueueClients(this, app); entity_list.QueueClients(this, app);
safe_delete(app); safe_delete(app);
SendPetBuffsToClient(); Buff::SendFullBuffRefresh(this);
SendAppearancePacket(AppearanceType::Pet, caster->GetID(), true, true); SendAppearancePacket(AppearanceType::Pet, caster->GetID(), true, true);
} }
@@ -3864,19 +3864,15 @@ void Mob::BuffProcess()
} }
} }
if(IsClient()) // this is for older clients. Newer clients will simply discard this packet
{ if (IsClient() && buffs[buffs_i].UpdateClient == true) {
if(buffs[buffs_i].UpdateClient == true) Buff::SendSingleBuffChange(this, buffs[buffs_i], buffs_i);
{ buffs[buffs_i].UpdateClient = false;
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;
}
} }
} }
} }
Buff::SendFullBuffRefresh(this);
} }
void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
@@ -4248,7 +4244,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
return; return;
if (IsClient() && !CastToClient()->IsDead()) 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); 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)) if (!TryFadeEffect(d))
BuffFadeBySlot(d, true); BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update } 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)) if (!TryFadeEffect(buff_slot))
BuffFadeBySlot(buff_slot , true); BuffFadeBySlot(buff_slot , true);
} else if (IsClient()) { // still have numhits and client, update } 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)) if (!TryFadeEffect(d))
BuffFadeBySlot(d, true); BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update } else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d); Buff::SendSingleBuffChange(this, buffs[d], d);
} }
} }
} }
+3 -146
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)) { if (IsClient() && IsEffectInSpell(spell_id, SpellEffect::BindSight)) {
for (int i = 0; i < GetMaxTotalSlots(); i++) { for (int i = 0; i < GetMaxTotalSlots(); i++) {
if (buffs[i].spellid == spell_id) { 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) { if (IsClient() && spells[spell_id].hit_number) {
for (int i = 0; i < GetMaxTotalSlots(); i++) { for (int i = 0; i < GetMaxTotalSlots(); i++) {
if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) { if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) {
CastToClient()->SendBuffNumHitPacket(buffs[i], i); Buff::SendSingleBuffChange(this, buffs[i], i);
break; break;
} }
} }
@@ -5809,53 +5809,6 @@ void Mob::Mesmerize()
StopNavigation(); 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) void Client::MemSpell(int32 spell_id, int slot, bool update_client)
{ {
if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) { if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) {
@@ -6501,99 +6454,6 @@ int Mob::GetCasterLevel(int32 spell_id) {
return std::max(1, level); 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) void Mob::BuffModifyDurationBySpellID(int32 spell_id, int32 newDuration)
{ {
int buff_count = GetMaxTotalSlots(); int buff_count = GetMaxTotalSlots();
@@ -6602,10 +6462,7 @@ void Mob::BuffModifyDurationBySpellID(int32 spell_id, int32 newDuration)
if (buffs[i].spellid == spell_id) if (buffs[i].spellid == spell_id)
{ {
buffs[i].ticsremaining = newDuration; buffs[i].ticsremaining = newDuration;
if(IsClient()) Buff::SendSingleBuffChange(this, buffs[i], i);
{
CastToClient()->SendBuffDurationPacket(buffs[i], i);
}
} }
} }
} }