Jump to content
View in the app

A better way to browse. Learn more.

Shaiya.gg

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

An effort to improve episode 6 support for private servers

Please read everything before you use anything in the repository. The library is designed to be reference material for people that back-engineer the game. You can view the source code here.

 

Thanks:

 

[mention=2]Cups[/mention] for being my inspiration and helping me to improve. I doubt this would exist without your contributions to this community.

[mention=25]joins.dj[/mention] for being a good friend and letting me pick your brain whenever I want.

[mention=26]Garret[/mention] for the Shaiya Essentials project.

[mention=5]cwifo[/mention], [mention=1]Shaco[/mention] and [mention=10]ItsBeno[/mention] for helping me test most of the features.

[mention=39]gualoki[/mention], [mention=17]melihxrist[/mention] and [mention=11]razor[/mention] for reporting critical bugs.

[mention=1]Shaco[/mention] for unpacking the 6.4 PT client.

[mention=35]rn4444[/mention] for testing the libraries and providing excellent feedback.

[mention=51]xarel[/mention] for several helpful bug reports.

 

Update:

 

I'll continue to update the project occasionally, but I'm not interested in adding new features. The issues tab will be disabled. I have a few reliable people who will let me know when something is wrong.

Edited by Bowie

  • Replies 95
  • Views 12.6k
  • Created
  • Last Reply

Top Posters In This Topic

Featured Replies

void CUser::AddApplySkillBuff(CUser* user, SkillInfo* skillInfo)
{
   typedef void(__stdcall* LPFN)(CUser*, SkillInfo*);
   (*(LPFN)0x494140)(user, skillInfo);
}

 

I tried using this to add skill buff to a player, it just triggered the animation of using the buff and it didn't add the buff icon.

 

auto charName = args.at(0);  // character name
auto skillId = std::stoi(args.at(1));
auto skillLevel = std::stoi(args.at(2));

auto user = CWorld::FindUser(charName.c_str());
if (!user) {
   std::stringstream output_format;
   output_format << "Character " << charName << " not found";
   output = output_format.str();
   return 0; // Exit if user is not found
}

auto skill = CGameData::GetSkillInfo(skillId, skillLevel);
if (!skill) {
   std::stringstream output_format;
   output_format << "Skill with ID " << skillId << " and level " << skillLevel << " not found";
   output = output_format.str();
   return 0; // Exit if skill is not found
}

// Apply the skill buff
CUser::AddApplySkillBuff(user, skill);

// Prepare the SkillUseOutgoing2 packet
SkillApplyOutgoing outgoing{};
outgoing.id = user->applySkills.last->id;
outgoing.skillId = skill->skillId;
outgoing.skillLv = skill->skillLv;

//Send the skill usage packet to the user
Helpers::Send(user, &outgoing, sizeof(SkillApplyOutgoing));

// Output a success message
std::stringstream output_format;
output_format << "Skill " << skillId << " (Level " << skillLevel << ") triggered for " << charName;
output = output_format.str();
return 0;

  • Author

[mention=38]Zhein[/mention]

 

How about this?

 

// Prepare the SkillUseOutgoing packet
SkillUseOutgoing outgoing{};
outgoing.senderId = user->connection.object.id;
outgoing.targetId = user->connection.object.id;
outgoing.skillId = skill->skillId;
outgoing.skillLv = skill->skillLv;

// Send the skill usage packet to the user
Helpers::Send(user, &outgoing, sizeof(SkillUseOutgoing));

// Apply the skill buff
CUser::AddApplySkillBuff(user, skill);

[mention=7]Bowie[/mention]

 

CUser::AddApplySkillBuff(user, skill);

 

Will this sends Packet 0x50D on its own or do I have to do it by myself?

 

I tried this

 

auto charName = args.at(0);  // character name
   auto skillId = std::stoi(args.at(1));
   auto skillLevel = std::stoi(args.at(2));

   auto user = CWorld::FindUser(charName.c_str());
   if (!user) {
       std::stringstream output_format;
       output_format << "Character " << charName << " not found";
       output = output_format.str();
       return 0; // Exit if user is not found
   }

   auto skill = CGameData::GetSkillInfo(skillId, skillLevel);
   if (!skill) {
       std::stringstream output_format;
       output_format << "Skill with ID " << skillId << " and level " << skillLevel << " not found";
       output = output_format.str();
       return 0; // Exit if skill is not found
   }

   // Prepare the SkillUseOutgoing packet
   SkillUseOutgoing outgoing{};
   outgoing.senderId = user->connection.object.id;
   outgoing.targetId = user->connection.object.id;
   outgoing.skillId = skill->skillId;
   outgoing.skillLv = skill->skillLv;

   // Send the skill usage packet to the user
   Helpers::Send(user, &outgoing, sizeof(SkillUseOutgoing));

   // Apply the skill buff
   CUser::AddApplySkillBuff(user, skill);

   EnterCriticalSection(&user->applySkills.cs);

   // Start from the first node after the sentinel tail
   auto node = user->applySkills.sentinel.tail;
   node = node->next;  // Move to the first node
   user->applySkills.sentinel.head = node;

   int index = 0; // Index to track the skill position in the list
   bool skillFound = false;

   while (node && node != user->applySkills.sentinel.tail)
   {
       auto skillIn = reinterpret_cast<CSkill*>(node);

       // Check if the skill matches
       if (skillIn->skillId == skill->skillId && skillIn->skillLv == skill->skillLv)
       {
           skillFound = true;

           // Correctly set the id for addBuff
           SkillApplyOutgoing addBuff{};
           addBuff.id = index; // Send the matching skill's index
           addBuff.skillId = skill->skillId;
           addBuff.skillLv = skill->skillLv;

           Helpers::Send(user, &addBuff, sizeof(SkillApplyOutgoing));
           break;
       }

       node = node->next;  // Move to the next node
       user->applySkills.sentinel.head = node;
       index++; // Increment index
   }

   LeaveCriticalSection(&user->applySkills.cs);

   if (!skillFound)
   {
       std::stringstream output_format;
       output_format << "Skill " << skillId << " (Level " << skillLevel << ") could not be applied for " << charName;
       output = output_format.str();
       return 0; // Exit after reporting error
   }

   // Output a success message
   std::stringstream output_format;
   output_format << "Skill " << skillId << " (Level " << skillLevel << ") triggered for " << charName;
   output = output_format.str();
   return 0;

 

Upon testing, it says Example

 

Skill 287 (Level 1) could not be applied for Character

  • Author

Will this sends Packet 0x50D on its own or do I have to do it by myself?

I don't see it sending that packet.

 

I think you should use "nostrum" skills and do it this way:

 

auto charName = args.at(0);  // character name
auto skillId = std::stoi(args.at(1));
auto skillLevel = std::stoi(args.at(2));

auto user = CWorld::FindUser(charName.c_str());
if (!user) {
   std::stringstream output_format;
   output_format << "Character " << charName << " not found";
   output = output_format.str();
   return 0; // Exit if user is not found
}

auto skill = CGameData::GetSkillInfo(skillId, skillLevel);
if (!skill) {
   std::stringstream output_format;
   output_format << "Skill with ID " << skillId << " and level " << skillLevel << " not found";
   output = output_format.str();
   return 0; // Exit if skill is not found
}

if (!CUser::UseItemSkill(user, skill)) {
   std::stringstream output_format;
   output_format << "Skill " << skillId << " (Level " << skillLevel << ") could not be applied for " << charName;
   output = output_format.str();
   return 0; // Exit after reporting error
}

// Output a success message
std::stringstream output_format;
output_format << "Skill " << skillId << " (Level " << skillLevel << ") triggered for " << charName;
output = output_format.str();
return 0;

 

I had to update the return type of UseItemSkill, so please change it from void to bool in your code.

Edited by Bowie

  • 2 weeks later...

[ATTACH type=full]91[/ATTACH]

 

 

I want to modify the code of this in the server side. Anyone knows the address to get started (in Essentail files)

  • Author

[mention=38]Zhein[/mention]

 

If you want to overwrite the case and write your own handler, you could set a detour at 0x47A010, with 8 bytes as the length and jmp to 0x47A018 at the end of the assembly function. The user is in edi and the packet is in esi.

 

[ATTACH type=full" alt="Capture.PNG]92[/ATTACH]

 

 

void item_remake_handler(CUser* user, ItemRemakeIncoming* incoming)
{
}

 

 

pushad

push esi // packet
push edi // user
call item_remake_handler
add esp,0x8

popad

jmp u0x47A018

Edited by Bowie

我以为该文件已附加到我的上一条消息中。🫤

如何操作?

  • Author

[mention=129]nurum[/mention]

 

Here's the source that was in the repo before I removed it.

[用户=129]@nurum[/用户]

 

这是我删除之前 repo 中的源代码。

我现在可以把它放进去并使用它吗?我们还需要调试吗?

[mention=7]Bowie[/mention], do you have discord?

 

void item_remake_handler(CUser* user, ItemRemakeIncoming* incoming) {


   // Check items in the specified slots
   auto* item1 = user->inventory[incoming->bag1][incoming->slot1];
   auto* item2 = user->inventory[incoming->bag2][incoming->slot2];
   auto* item3 = user->inventory[incoming->bag3][incoming->slot3];


   // Ensure all items are present
   if (!item1 || !item2 || !item3) {
       Helpers::SendNotice("LOG: One or more items are missing.");
       return;
   }


   // Ensure all items have the same ID
   if (item1->itemInfo->itemId != item2->itemInfo->itemId ||
       item1->itemInfo->itemId != item3->itemInfo->itemId) {
       Helpers::SendNotice("LOG: Items do not match.");
       return;
   }


   // Handle specific item creation
   ItemInfo* resultItem = nullptr;
   if (item1->itemInfo->itemId == 72001) {
       int resultType = 72;
       int resultTypeId = 2;
       resultItem = CGameData::GetItemInfo(resultType, resultTypeId);
       if (resultItem) {
           std::string resultLog = "LOG: Created result item with Type: " +
           std::to_string(resultType) + ", TypeID: " + std::to_string(resultTypeId);
           Helpers::SendNotice(resultLog.c_str());
       }
       else {
           Helpers::SendNotice("LOG: Failed to create result item.");
           return;
       }
   }
   else {
       Helpers::SendNotice("LOG: Item ID does not match special handling conditions.");


       ItemRemakeOutgoing outgoing{};
       outgoing.result = ItemRemakeResult::Failure;
       Helpers::Send(user, &outgoing, sizeof(ItemRemakeOutgoing));


       return;
   }


   
   uint8_t bag = 1; // Result item bag
   uint8_t slot = -1; // Result item slot
   while (bag <= user->bagsUnlocked) {
       slot = Helpers::GetFreeItemSlot(user, bag);
       if (slot != -1) {
           if (CUser::ItemCreate(user, resultItem, 1)) {
               break;
           }
       }
       ++bag;
   }


   // Validate the resulting item placement
   auto* findResult = user->inventory[bag][slot];
   if (!findResult) {
       Helpers::SendNotice("LOG: Result item was not created.");
       return;
   }


   // Transfer the details of the first item to the result
   findResult->gems = item1->gems;
   findResult->craftName = item1->craftName;
   findResult->quality = item1->quality;


   // Assign the findResult back to the inventory
   user->inventory[bag][slot] = findResult;


   //Log the inventory[bag][slot] if already have its gems, craftname and quality
   Helpers::ItemRemove(user, item1->itemInfo->itemId, 1);
   Helpers::ItemRemove(user, item2->itemInfo->itemId, 1);
   Helpers::ItemRemove(user, item3->itemInfo->itemId, 1);


   // Send outgoing packet with result details
   ItemRemakeOutgoing outgoing{};
   outgoing.result = ItemRemakeResult::Success;
   outgoing.bag = bag;
   outgoing.slot = slot;
   outgoing.type = resultItem->type;
   outgoing.typeId = resultItem->typeId;
   outgoing.gems = findResult->gems;
   outgoing.count = findResult->count;
   outgoing.craftName = findResult->craftName;
   Helpers::Send(user, &outgoing, sizeof(ItemRemakeOutgoing));


   GameLogItemRemakeIncoming remakeLog(user, findResult, item1->uniqueId, item2->uniqueId, item3->uniqueId);
   Helpers::SendGameLog(&remakeLog, sizeof(GameLogItemRemakeIncoming));


   Helpers::SendNotice("LOG: Handler completed successfully.");


}

 

Can I ask why the item gems, craftname and quality is not persistent?

  • Author

Can I ask why the item gems, craftname and quality is not persistent?

 

You have to send a packet to the dbAgent service. Looks like the remake function sends 0x701. I added it to the library.

 

Please add range checks for the bags and slots. :p

 

Edit: sending 0x711 and 0x717 to the dbAgent service resolved the issue.

Edited by Bowie

有人卖光环效果吗?

Edited by nurum

  • 3 months later...

有人卖光环效果吗?

输入:加我QQ:592372215 加的时候备注下

  • 3 weeks later...

@[mention=7]Bowie[/mention] hi i find the Item Ability Transfer have Bug in attribute overlay. The attributes of the equipment worn on the body are transferred to the new equipment, and then the new equipment is put on. It will add the attribute of additional gems embedded in the equipment.

Repeating this operation can infinitely increase the attributes of the character.

this bug Bowie fix into v1.6.5

Edited by rn4444

  • 4 weeks later...

[mention=129]nurum[/mention]

 

Here's the source that was in the repo before I removed it.

It's a shame it was removed :(

Edited by Saiker

  • 5 months later...

You have to send a packet to the dbAgent service. Looks like the remake function sends 0x701. I added it to the library.

 

Please add range checks for the bags and slots. :p

 

Edit: sending 0x711 and 0x717 to the dbAgent service resolved the issue.

Any idea on how to handle different item type/typeids for each of the 3 slots ? For example, when I put 3 same items my packet handler is executed but if I put 3 different items nothing happens.

Create an account or sign in to comment

Account

Navigation

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.