Support for client version 7.6

Tell me if there are parts which I should change.

- Not everything is tested yet, feel free to report bugs in 7.6
- the data send in between login opcode and account data might not be
correct, but I could not find any documents showing the right protocol
yet
- Hotkeys working
This commit is contained in:
Sam
2013-10-04 04:09:54 +02:00
parent 7cf645e715
commit 5843b78e87
14 changed files with 240 additions and 111 deletions

View File

@@ -355,6 +355,15 @@ namespace Otc
GameDoubleShopSellAmount = 39,
GameContainerPagination = 40,
GameThingMarks = 41,
GameLooktypeU16 = 42,
GamePlayerStamina = 43,
GamePlayerAddons = 44,
GameMessageStatments = 45,
GameMessageLevel = 46,
GameNewFluids = 47,
GamePlayerStateU16 = 48,
// 51-100 reserved to be defined in lua
LastGameFeature = 101
};

View File

@@ -59,6 +59,14 @@ void Container::onAddItem(const ItemPtr& item)
callLuaField("onAddItem", 0, item);
}
ItemPtr Container::findItemById(uint itemId, int subType)
{
for(const ItemPtr item : m_items)
if(item->getId() == itemId && (subType == -1 || item->getSubType() == subType))
return item;
return nullptr;
}
void Container::onAddItems(const std::vector<ItemPtr>& items)
{
for(const ItemPtr& item : items)

View File

@@ -45,6 +45,7 @@ public:
std::string getName() { return m_name; }
bool hasParent() { return m_hasParent; }
bool isClosed() { return m_closed; }
ItemPtr findItemById(uint itemId, int subType);
protected:
void onOpen(const ContainerPtr& previousContainer);

View File

@@ -884,7 +884,7 @@ void Game::useWith(const ItemPtr& item, const ThingPtr& toThing)
if(!pos.isValid()) // virtual item
pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
if(toThing->isCreature())
if(toThing->isCreature() && g_game.getProtocolVersion() >= 780)
m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackPos(), toThing->getId());
else
m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackPos(), toThing->getPosition(), toThing->getId(), toThing->getStackPos());
@@ -903,6 +903,20 @@ void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing)
m_protocolGame->sendUseItemWith(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toThing->getStackPos());
}
ItemPtr Game::findItemInContainers(uint itemId, int subType)
{
for(auto& it : m_containers) {
const ContainerPtr& container = it.second;
if(container) {
ItemPtr item = container->findItemById(itemId, subType);
if(item != nullptr)
return item;
}
}
return nullptr;
}
int Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
{
if(!canPerformGameAction() || !item)
@@ -1400,19 +1414,31 @@ void Game::setProtocolVersion(int version)
if(isOnline())
stdext::throw_exception("Unable to change protocol version while online");
if(version != 0 && (version < 810 || version > 1010))
if(version != 0 && version != 760 && (version < 810 || version > 1010))
stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_features.reset();
enableFeature(Otc::GameFormatCreatureName);
if(version >= 780)
{
enableFeature(Otc::GamePlayerAddons);
enableFeature(Otc::GamePlayerStamina);
enableFeature(Otc::GameNewFluids);
enableFeature(Otc::GameMessageLevel);
enableFeature(Otc::GameMessageStatments);
enableFeature(Otc::GamePlayerStateU16);
enableFeature(Otc::GameLooktypeU16);
}
if(version >= 840) {
enableFeature(Otc::GameProtocolChecksum);
enableFeature(Otc::GameChallengeOnLogin);
enableFeature(Otc::GameAccountNames);
}
if(version <= 854) {
if(version >= 780 && version <= 854) { // 780 might not be accurate
enableFeature(Otc::GameChargeableItems);
}
@@ -1487,7 +1513,7 @@ void Game::setClientVersion(int version)
if(isOnline())
stdext::throw_exception("Unable to change client version while online");
if(version != 0 && (version < 810 || version > 1010))
if(version != 0 && version != 760 && (version < 810 || version > 1010))
stdext::throw_exception(stdext::format("Client version %d not supported", version));
m_clientVersion = version;

View File

@@ -158,6 +158,7 @@ public:
void useWith(const ItemPtr& fromThing, const ThingPtr& toThing);
void useInventoryItem(int itemId);
void useInventoryItemWith(int itemId, const ThingPtr& toThing);
ItemPtr findItemInContainers(uint itemId, int subType);
// container related
int open(const ItemPtr& item, const ContainerPtr& previousContainer);

View File

@@ -288,65 +288,68 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern)
}
} else if(isSplash() || isFluidContainer()) {
int color = Otc::FluidTransparent;
switch(m_countOrSubType) {
case Otc::FluidNone:
color = Otc::FluidTransparent;
break;
case Otc::FluidWater:
color = Otc::FluidBlue;
break;
case Otc::FluidMana:
color = Otc::FluidPurple;
break;
case Otc::FluidBeer:
color = Otc::FluidBrown;
break;
case Otc::FluidOil:
color = Otc::FluidBrown;
break;
case Otc::FluidBlood:
color = Otc::FluidRed;
break;
case Otc::FluidSlime:
color = Otc::FluidGreen;
break;
case Otc::FluidMud:
color = Otc::FluidBrown;
break;
case Otc::FluidLemonade:
color = Otc::FluidYellow;
break;
case Otc::FluidMilk:
color = Otc::FluidWhite;
break;
case Otc::FluidWine:
color = Otc::FluidPurple;
break;
case Otc::FluidHealth:
color = Otc::FluidRed;
break;
case Otc::FluidUrine:
color = Otc::FluidYellow;
break;
case Otc::FluidRum:
color = Otc::FluidBrown;
break;
case Otc::FluidFruidJuice:
color = Otc::FluidYellow;
break;
case Otc::FluidCoconutMilk:
color = Otc::FluidWhite;
break;
case Otc::FluidTea:
color = Otc::FluidBrown;
break;
case Otc::FluidMead:
color = Otc::FluidBrown;
break;
default:
color = Otc::FluidTransparent;
break;
}
if(g_game.getFeature(Otc::GameNewFluids)) {
switch(m_countOrSubType) {
case Otc::FluidNone:
color = Otc::FluidTransparent;
break;
case Otc::FluidWater:
color = Otc::FluidBlue;
break;
case Otc::FluidMana:
color = Otc::FluidPurple;
break;
case Otc::FluidBeer:
color = Otc::FluidBrown;
break;
case Otc::FluidOil:
color = Otc::FluidBrown;
break;
case Otc::FluidBlood:
color = Otc::FluidRed;
break;
case Otc::FluidSlime:
color = Otc::FluidGreen;
break;
case Otc::FluidMud:
color = Otc::FluidBrown;
break;
case Otc::FluidLemonade:
color = Otc::FluidYellow;
break;
case Otc::FluidMilk:
color = Otc::FluidWhite;
break;
case Otc::FluidWine:
color = Otc::FluidPurple;
break;
case Otc::FluidHealth:
color = Otc::FluidRed;
break;
case Otc::FluidUrine:
color = Otc::FluidYellow;
break;
case Otc::FluidRum:
color = Otc::FluidBrown;
break;
case Otc::FluidFruidJuice:
color = Otc::FluidYellow;
break;
case Otc::FluidCoconutMilk:
color = Otc::FluidWhite;
break;
case Otc::FluidTea:
color = Otc::FluidBrown;
break;
case Otc::FluidMead:
color = Otc::FluidBrown;
break;
default:
color = Otc::FluidTransparent;
break;
}
} else
color = m_countOrSubType;
xPattern = (color % 4) % getNumPatternX();
yPattern = (color / 4) % getNumPatternY();

View File

@@ -184,6 +184,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "useWith", &Game::useWith, &g_game);
g_lua.bindSingletonFunction("g_game", "useInventoryItem", &Game::useInventoryItem, &g_game);
g_lua.bindSingletonFunction("g_game", "useInventoryItemWith", &Game::useInventoryItemWith, &g_game);
g_lua.bindSingletonFunction("g_game", "findItemInContainers", &Game::findItemInContainers, &g_game);
g_lua.bindSingletonFunction("g_game", "open", &Game::open, &g_game);
g_lua.bindSingletonFunction("g_game", "openParent", &Game::openParent, &g_game);
g_lua.bindSingletonFunction("g_game", "close", &Game::close, &g_game);

View File

@@ -87,7 +87,7 @@ void buildMessageModesMap(int version) {
messageModesMap[Otc::MessageLook] = 25;
messageModesMap[Otc::MessageFailure] = 26;
messageModesMap[Otc::MessageBlue] = 27;
} else if(version >= 810) {
} else if(version >= 760) {
messageModesMap[Otc::MessageNone] = 0;
messageModesMap[Otc::MessageSay] = 1;
messageModesMap[Otc::MessageWhisper] = 2;

View File

@@ -1038,7 +1038,9 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
double magicLevelPercent = msg->getU8();
double soul = msg->getU8();
double stamina = msg->getU16();
double stamina = 0;
if(g_game.getFeature(Otc::GamePlayerStamina))
stamina = msg->getU16();
double baseSpeed = 0;
if(g_game.getFeature(Otc::GameSkillsBase))
@@ -1092,7 +1094,12 @@ void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
{
int states = msg->getU16();
int states;
if(g_game.getFeature(Otc::GamePlayerStateU16))
states = msg->getU16();
else
states = msg->getU8();
m_localPlayer->setStates(states);
}
@@ -1130,10 +1137,15 @@ void ProtocolGame::parseMultiUseCooldown(const InputMessagePtr& msg)
void ProtocolGame::parseTalk(const InputMessagePtr& msg)
{
msg->getU32(); // channel statement guid
if(g_game.getFeature(Otc::GameMessageStatments))
msg->getU32(); // channel statement guid
std::string name = g_game.formatCreatureName(msg->getString());
int level = msg->getU16();
int level = 0;
if(g_game.getFeature(Otc::GameMessageLevel))
level = msg->getU16();
Otc::MessageMode mode = Proto::translateMessageModeFromServer(msg->getU8());
int channelId = 0;
Position pos;
@@ -1380,15 +1392,23 @@ void ProtocolGame::parseFloorChangeDown(const InputMessagePtr& msg)
void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg)
{
Outfit currentOutfit = getOutfit(msg);
std::vector<std::tuple<int, std::string, int> > outfitList;
int outfitCount = msg->getU8();
for(int i = 0; i < outfitCount; i++) {
int outfitId = msg->getU16();
std::string outfitName = msg->getString();
int outfitAddons = msg->getU8();
outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons));
if(g_game.getProtocolVersion() < 780) {
int outfitStart = msg->getU8();
int outfitEnd = msg->getU8();
for(int i = outfitStart; i <= outfitEnd; i++)
outfitList.push_back(std::make_tuple(i, "", 0));
}
else {
int outfitCount = msg->getU8();
for(int i = 0; i < outfitCount; i++) {
int outfitId = msg->getU16();
std::string outfitName = msg->getString();
int outfitAddons = msg->getU8();
outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons));
}
}
std::vector<std::tuple<int, std::string> > mountList;
@@ -1665,14 +1685,21 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
{
Outfit outfit;
int lookType = msg->getU16();
int lookType;
if(g_game.getFeature(Otc::GameLooktypeU16))
lookType = msg->getU16();
else
lookType = msg->getU8();
if(lookType != 0) {
outfit.setCategory(ThingCategoryCreature);
int head = msg->getU8();
int body = msg->getU8();
int legs = msg->getU8();
int feet = msg->getU8();
int addons = msg->getU8();
int addons = 0;
if(g_game.getFeature(Otc::GamePlayerAddons))
addons = msg->getU8();
if(!g_things.isValidDatId(lookType, ThingCategoryCreature)) {
g_logger.traceError(stdext::format("invalid outfit looktype %d", lookType));

View File

@@ -52,8 +52,16 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
{
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientPendingGame);
msg->addU16(g_game.getOs());
if(g_game.getProtocolVersion() == 760)
{
msg->addU16(0x20A);
msg->addU8(g_game.getOs());
}
else
{
msg->addU8(Proto::ClientPendingGame);
msg->addU16(g_game.getOs());
}
msg->addU16(g_game.getProtocolVersion());
if(g_game.getProtocolVersion() >= 971) {
@@ -65,13 +73,16 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addU8(0); // first RSA byte must be 0
// xtea key
generateXteaKey();
msg->addU32(m_xteaKey[0]);
msg->addU32(m_xteaKey[1]);
msg->addU32(m_xteaKey[2]);
msg->addU32(m_xteaKey[3]);
msg->addU8(0); // is gm set?
if(g_game.getProtocolVersion() >= 800)
{
// xtea key
generateXteaKey();
msg->addU32(m_xteaKey[0]);
msg->addU32(m_xteaKey[1]);
msg->addU32(m_xteaKey[2]);
msg->addU32(m_xteaKey[3]);
msg->addU8(0); // is gm set?
}
if(g_game.getFeature(Otc::GameAccountNames))
msg->addString(m_accountName);
@@ -96,14 +107,16 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addPaddingBytes(paddingBytes);
// encrypt with RSA
msg->encryptRsa();
if(g_game.getProtocolVersion() >= 800)
msg->encryptRsa();
if(g_game.getFeature(Otc::GameProtocolChecksum))
enableChecksum();
send(msg);
enableXteaEncryption();
if(g_game.getProtocolVersion() >= 800)
enableXteaEncryption();
}
void ProtocolGame::sendEnterGame()