-
Notifications
You must be signed in to change notification settings - Fork 536
Description
The emulator currently supports blizzlike behavior when attempting to read items or objects. You either can read it, or you can't. Language features are not supported in the emulator because they were never really used in vanilla, short of binary checks for known languages.
However, language systems in the client are complex, and in terms of blizzard's backend, the handlers undoubtedly exist. The client supports a range of language skill, from 1-300, and algorithmically translates pagetext based on the characters language skill. In vanilla, this value is always either 300, or you simply dont know the language.
In terms of the backend handlers for SMSG_READ_ITEM_FAILED, there is an unimplemented portion of the packet structure that could be better handled internally to enable such features. The logic can follow vanilla blizzlike behavior in that these handlers will never reach the code.
Here is an example of the corrected structure:
Opcode: SMSG_READ_ITEM_FAILED
ObjectGuid (uint64)
State [0..2] (uint8)
Timer (uint32)
Currently in VMaNGOS, when reading a pagetext item/object, it should always return EQUIP_ERR_OK and send SMSG_READ_ITEM_OK.
It currently will never (or shouldn't, without error) send SMSG_READ_ITEM_FAILED as checks for using items/gameobjects are handled elsewhere. VMaNGOS currently only has handling for State [0] and does not include the timer at all. This packet can actually have three different states which the client handles, one of which utilizes the Timer (uint32):
State [0] = Send text; client is marked to translate it based on skill level
State [1] = Send text; do not translate; initiate timer instead
State [2] = Do not send text; client does not open pagetext panel
It's a complicated packet to understand and properly implement, mostly because we can only speculate what the behavior should be (as this was simplified before vanilla launch). As it stands, the packet for SMSG_READ_ITEM_FAILED should probably return STATE [2] so that the client does not even attempt to open the pagetext; If they somehow managed to reach SMSG_READ_ITEM_FAILED and they do not understand the language, it will send untranslated pagetext to the client.
As for how it actually worked, someone in wowmodding posted a video of their experimentation and I managed to reverse it and implement it into VMaNGOS natively:
void WorldSession::HandleReadItemOpcode(WorldPacket& recv_data)
{
uint8 bag, slot;
recv_data >> bag >> slot;
Item *pItem = _player->GetItemByPos(bag, slot);
if (pItem && pItem->GetProto()->PageText)
{
WorldPacket data;
InventoryResult msg = _player->CanUseItem(pItem);
if (msg == EQUIP_ERR_OK)
{
if (uint16 skill = _player->GetSkillForLanguage(pItem->GetProto()->LanguageID))
{
if (uint16 skillValue = _player->GetSkillValue(skill))
{
if (uint16 skillMax = _player->GetSkillMax(skill))
{
data.Initialize(SMSG_READ_ITEM_FAILED, 8 + 1 + 4);
data << ObjectGuid(pItem->GetObjectGuid());
data << uint8(1);
data << uint32(5000);
SendPacket(&data);
_player->m_Events.AddLambdaEventAtOffset(
[this, pItem]
{
WorldPacket data2;
data2.Initialize(SMSG_READ_ITEM_FAILED, 8 + 1);
data2 << ObjectGuid(pItem->GetObjectGuid());
data2 << uint8(0);
SendPacket(&data2);
},
5000);
}
else
_player->SendEquipError(EQUIP_ERR_NO_REQUIRED_PROFICIENCY, pItem);
}
else
_player->SendEquipError(EQUIP_ERR_NO_REQUIRED_PROFICIENCY, pItem);
}
else
{
data.Initialize(SMSG_READ_ITEM_OK, 8);
data << ObjectGuid(pItem->GetObjectGuid());
SendPacket(&data);
}
}
else if (msg == EQUIP_ERR_NO_REQUIRED_PROFICIENCY)
{
data.Initialize(SMSG_READ_ITEM_FAILED, 8 + 1);
data << ObjectGuid(pItem->GetObjectGuid());
data << uint8(2);
SendPacket(&data);
_player->SendEquipError(EQUIP_ERR_NO_REQUIRED_PROFICIENCY, pItem);
}
else
_player->SendEquipError(EQUIP_ERR_NO_REQUIRED_PROFICIENCY, pItem);
}
else
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr);
}As you can see, if language systems are enabled in the client, this code will handle it as best as we can assume the behavior to be. There are also handlers implemented in CanUseItem() to check if the player knows the language; if not, CanUseItem() returns EQUIP_ERR_NO_REQUIRED_PROFICIENCY and SMSG_READ_ITEM_FAILED is sent with State [2] (do not open page text panel; failure)
If the player knows the language, it will then check the skill for the language (a new function with a switch enumerating all the Skill-Language pairings). It will then check the player's skill level and skill max for validity. Afterwards, it sends SMSG_READ_ITEM_FAILED with State [1] and a Timer (uint32) set to 5000ms (guessed value). This sends the page text to the client, marking it as untranslated from its origin language, along with a progress bar on the page text with the timer value in milliseconds.
It then creates a Lambda event with the same timer; After the timer expires, it sends SMSG_READ_ITEM_FAILED State [0] (translate page text based on client skill value).
This creates the effect of the page text being translated after the player character reads the letter. As stated, all of these functions exist natively in the client but were scrapped before vanilla launched. They existed until WoD when the client had many of the functions reworked or removed entirely. We can never truly know for certain how the behavior was precisely handled, and the function need not exist in such form to emulate Blizzlike vanilla. Nor do I expect any of this to be implemented in any way. But it's something that Brotalnia will find interesting, as it sheds light on more internal structures that we don't fully understand yet.