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

@ -375,15 +375,41 @@ function doKeyCombo(keyCombo)
modules.game_console.setTextEditText(hotKey.value) modules.game_console.setTextEditText(hotKey.value)
end end
elseif hotKey.useType == HOTKEY_MANAGER_USE then elseif hotKey.useType == HOTKEY_MANAGER_USE then
if g_game.getProtocolVersion() < 780 then
local item = g_game.findItemInContainers(hotKey.itemId, -1)
if item then
g_game.use(item)
end
else
g_game.useInventoryItem(hotKey.itemId) g_game.useInventoryItem(hotKey.itemId)
end
elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then
if g_game.getProtocolVersion() < 780 then
local item = g_game.findItemInContainers(hotKey.itemId, -1)
if item then
g_game.useWith(item, g_game.getLocalPlayer())
end
else
g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer()) g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer())
end
elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then
local attackingCreature = g_game.getAttackingCreature() local attackingCreature = g_game.getAttackingCreature()
if not attackingCreature then return end if not attackingCreature then return end
if g_game.getProtocolVersion() < 780 then
local item = g_game.findItemInContainers(hotKey.itemId, -1)
if item then
g_game.useWith(item, attackingCreature)
end
else
g_game.useInventoryItemWith(hotKey.itemId, attackingCreature) g_game.useInventoryItemWith(hotKey.itemId, attackingCreature)
end
elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then
local item = Item.create(hotKey.itemId) local item = Item.create(hotKey.itemId)
if g_game.getProtocolVersion() < 780 then
local tmpItem = g_game.findItemInContainers(hotKey.itemId, -1)
if not tmpItem then return true end
item = tmpItem
end
modules.game_interface.startUseWith(item) modules.game_interface.startUseWith(item)
end end
end end

View File

@ -34,11 +34,11 @@ end
function g_game.getSupportedClients() function g_game.getSupportedClients()
return { return {
810, 811, 840, 842, 850, 853, 854, 760, 810, 811, 840, 842, 850, 853,
860, 861, 862, 870, 910, 940, 944, 854, 860, 861, 862, 870, 910, 940,
953, 954, 960, 961, 963, 970, 980, 944, 953, 954, 960, 961, 963, 970,
981, 982, 983, 984, 985, 986, 1001, 980, 981, 982, 983, 984, 985, 986,
1002, 1010 1001, 1002, 1010
} }
end end

View File

@ -188,5 +188,9 @@ ClientOpcodes = {
ClientMarketCreate = 246, -- 944 ClientMarketCreate = 246, -- 944
ClientMarketCancel = 247, -- 944 ClientMarketCancel = 247, -- 944
ClientMarketAccept = 248, -- 944 ClientMarketAccept = 248, -- 944
ClientAnswerModalDialog = 249 -- 960 ClientAnswerModalDialog = 249, -- 960
-- 760
ClientEnterAccount760 = 513,
ClientEnterGame760 = 522
} }

View File

@ -27,9 +27,13 @@ end
function ProtocolLogin:sendLoginPacket() function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create() local msg = OutputMessage.create()
if g_game.getProtocolVersion() == 760 then
msg:addU16(ClientOpcodes.ClientEnterAccount760)
else
msg:addU8(ClientOpcodes.ClientEnterAccount) msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOs()) msg:addU16(g_game.getOs())
end
msg:addU16(g_game.getProtocolVersion()) msg:addU16(g_game.getProtocolVersion())
if g_game.getProtocolVersion() >= 971 then if g_game.getProtocolVersion() >= 971 then
@ -49,6 +53,7 @@ function ProtocolLogin:sendLoginPacket()
-- first RSA byte must be 0 -- first RSA byte must be 0
msg:addU8(0) msg:addU8(0)
if g_game.getProtocolVersion() >= 800 then
-- xtea key -- xtea key
self:generateXteaKey() self:generateXteaKey()
local xteaKey = self:getXteaKey() local xteaKey = self:getXteaKey()
@ -56,6 +61,7 @@ function ProtocolLogin:sendLoginPacket()
msg:addU32(xteaKey[2]) msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3]) msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4]) msg:addU32(xteaKey[4])
end
if g_game.getFeature(GameAccountNames) then if g_game.getFeature(GameAccountNames) then
msg:addString(self.accountName) msg:addString(self.accountName)
@ -73,14 +79,18 @@ function ProtocolLogin:sendLoginPacket()
local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset) local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0) assert(paddingBytes >= 0)
msg:addPaddingBytes(paddingBytes, 0) msg:addPaddingBytes(paddingBytes, 0)
if g_game.getProtocolVersion() >= 800 then
msg:encryptRsa() msg:encryptRsa()
end
if g_game.getFeature(GameProtocolChecksum) then if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum() self:enableChecksum()
end end
self:send(msg) self:send(msg)
if g_game.getProtocolVersion() >= 800 then
self:enableXteaEncryption() self:enableXteaEncryption()
end
self:recv() self:recv()
end end

View File

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

View File

@ -59,6 +59,14 @@ void Container::onAddItem(const ItemPtr& item)
callLuaField("onAddItem", 0, 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) void Container::onAddItems(const std::vector<ItemPtr>& items)
{ {
for(const ItemPtr& item : items) for(const ItemPtr& item : items)

View File

@ -45,6 +45,7 @@ public:
std::string getName() { return m_name; } std::string getName() { return m_name; }
bool hasParent() { return m_hasParent; } bool hasParent() { return m_hasParent; }
bool isClosed() { return m_closed; } bool isClosed() { return m_closed; }
ItemPtr findItemById(uint itemId, int subType);
protected: protected:
void onOpen(const ContainerPtr& previousContainer); 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 if(!pos.isValid()) // virtual item
pos = Position(0xFFFF, 0, 0); // means that is a item in inventory 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()); m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackPos(), toThing->getId());
else else
m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackPos(), toThing->getPosition(), toThing->getId(), toThing->getStackPos()); 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()); 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) int Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
{ {
if(!canPerformGameAction() || !item) if(!canPerformGameAction() || !item)
@ -1400,19 +1414,31 @@ void Game::setProtocolVersion(int version)
if(isOnline()) if(isOnline())
stdext::throw_exception("Unable to change protocol version while online"); 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)); stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_features.reset(); m_features.reset();
enableFeature(Otc::GameFormatCreatureName); 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) { if(version >= 840) {
enableFeature(Otc::GameProtocolChecksum); enableFeature(Otc::GameProtocolChecksum);
enableFeature(Otc::GameChallengeOnLogin); enableFeature(Otc::GameChallengeOnLogin);
enableFeature(Otc::GameAccountNames); enableFeature(Otc::GameAccountNames);
} }
if(version <= 854) { if(version >= 780 && version <= 854) { // 780 might not be accurate
enableFeature(Otc::GameChargeableItems); enableFeature(Otc::GameChargeableItems);
} }
@ -1487,7 +1513,7 @@ void Game::setClientVersion(int version)
if(isOnline()) if(isOnline())
stdext::throw_exception("Unable to change client version while online"); 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)); stdext::throw_exception(stdext::format("Client version %d not supported", version));
m_clientVersion = version; m_clientVersion = version;

View File

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

View File

@ -288,6 +288,7 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern)
} }
} else if(isSplash() || isFluidContainer()) { } else if(isSplash() || isFluidContainer()) {
int color = Otc::FluidTransparent; int color = Otc::FluidTransparent;
if(g_game.getFeature(Otc::GameNewFluids)) {
switch(m_countOrSubType) { switch(m_countOrSubType) {
case Otc::FluidNone: case Otc::FluidNone:
color = Otc::FluidTransparent; color = Otc::FluidTransparent;
@ -347,6 +348,8 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern)
color = Otc::FluidTransparent; color = Otc::FluidTransparent;
break; break;
} }
} else
color = m_countOrSubType;
xPattern = (color % 4) % getNumPatternX(); xPattern = (color % 4) % getNumPatternX();
yPattern = (color / 4) % getNumPatternY(); 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", "useWith", &Game::useWith, &g_game);
g_lua.bindSingletonFunction("g_game", "useInventoryItem", &Game::useInventoryItem, &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", "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", "open", &Game::open, &g_game);
g_lua.bindSingletonFunction("g_game", "openParent", &Game::openParent, &g_game); g_lua.bindSingletonFunction("g_game", "openParent", &Game::openParent, &g_game);
g_lua.bindSingletonFunction("g_game", "close", &Game::close, &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::MessageLook] = 25;
messageModesMap[Otc::MessageFailure] = 26; messageModesMap[Otc::MessageFailure] = 26;
messageModesMap[Otc::MessageBlue] = 27; messageModesMap[Otc::MessageBlue] = 27;
} else if(version >= 810) { } else if(version >= 760) {
messageModesMap[Otc::MessageNone] = 0; messageModesMap[Otc::MessageNone] = 0;
messageModesMap[Otc::MessageSay] = 1; messageModesMap[Otc::MessageSay] = 1;
messageModesMap[Otc::MessageWhisper] = 2; messageModesMap[Otc::MessageWhisper] = 2;

View File

@ -1038,7 +1038,9 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
double magicLevelPercent = msg->getU8(); double magicLevelPercent = msg->getU8();
double soul = 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; double baseSpeed = 0;
if(g_game.getFeature(Otc::GameSkillsBase)) if(g_game.getFeature(Otc::GameSkillsBase))
@ -1092,7 +1094,12 @@ void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerState(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); m_localPlayer->setStates(states);
} }
@ -1130,10 +1137,15 @@ void ProtocolGame::parseMultiUseCooldown(const InputMessagePtr& msg)
void ProtocolGame::parseTalk(const InputMessagePtr& msg) void ProtocolGame::parseTalk(const InputMessagePtr& msg)
{ {
if(g_game.getFeature(Otc::GameMessageStatments))
msg->getU32(); // channel statement guid msg->getU32(); // channel statement guid
std::string name = g_game.formatCreatureName(msg->getString()); 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()); Otc::MessageMode mode = Proto::translateMessageModeFromServer(msg->getU8());
int channelId = 0; int channelId = 0;
Position pos; Position pos;
@ -1380,8 +1392,15 @@ void ProtocolGame::parseFloorChangeDown(const InputMessagePtr& msg)
void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg) void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg)
{ {
Outfit currentOutfit = getOutfit(msg); Outfit currentOutfit = getOutfit(msg);
std::vector<std::tuple<int, std::string, int> > outfitList; std::vector<std::tuple<int, std::string, int> > outfitList;
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(); int outfitCount = msg->getU8();
for(int i = 0; i < outfitCount; i++) { for(int i = 0; i < outfitCount; i++) {
int outfitId = msg->getU16(); int outfitId = msg->getU16();
@ -1390,6 +1409,7 @@ void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg)
outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons)); outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons));
} }
}
std::vector<std::tuple<int, std::string> > mountList; std::vector<std::tuple<int, std::string> > mountList;
if(g_game.getFeature(Otc::GamePlayerMounts)) { if(g_game.getFeature(Otc::GamePlayerMounts)) {
@ -1665,14 +1685,21 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
{ {
Outfit outfit; Outfit outfit;
int lookType = msg->getU16(); int lookType;
if(g_game.getFeature(Otc::GameLooktypeU16))
lookType = msg->getU16();
else
lookType = msg->getU8();
if(lookType != 0) { if(lookType != 0) {
outfit.setCategory(ThingCategoryCreature); outfit.setCategory(ThingCategoryCreature);
int head = msg->getU8(); int head = msg->getU8();
int body = msg->getU8(); int body = msg->getU8();
int legs = msg->getU8(); int legs = msg->getU8();
int feet = 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)) { if(!g_things.isValidDatId(lookType, ThingCategoryCreature)) {
g_logger.traceError(stdext::format("invalid outfit looktype %d", lookType)); 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); OutputMessagePtr msg(new OutputMessage);
if(g_game.getProtocolVersion() == 760)
{
msg->addU16(0x20A);
msg->addU8(g_game.getOs());
}
else
{
msg->addU8(Proto::ClientPendingGame); msg->addU8(Proto::ClientPendingGame);
msg->addU16(g_game.getOs()); msg->addU16(g_game.getOs());
}
msg->addU16(g_game.getProtocolVersion()); msg->addU16(g_game.getProtocolVersion());
if(g_game.getProtocolVersion() >= 971) { if(g_game.getProtocolVersion() >= 971) {
@ -65,6 +73,8 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addU8(0); // first RSA byte must be 0 msg->addU8(0); // first RSA byte must be 0
if(g_game.getProtocolVersion() >= 800)
{
// xtea key // xtea key
generateXteaKey(); generateXteaKey();
msg->addU32(m_xteaKey[0]); msg->addU32(m_xteaKey[0]);
@ -72,6 +82,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addU32(m_xteaKey[2]); msg->addU32(m_xteaKey[2]);
msg->addU32(m_xteaKey[3]); msg->addU32(m_xteaKey[3]);
msg->addU8(0); // is gm set? msg->addU8(0); // is gm set?
}
if(g_game.getFeature(Otc::GameAccountNames)) if(g_game.getFeature(Otc::GameAccountNames))
msg->addString(m_accountName); msg->addString(m_accountName);
@ -96,6 +107,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addPaddingBytes(paddingBytes); msg->addPaddingBytes(paddingBytes);
// encrypt with RSA // encrypt with RSA
if(g_game.getProtocolVersion() >= 800)
msg->encryptRsa(); msg->encryptRsa();
if(g_game.getFeature(Otc::GameProtocolChecksum)) if(g_game.getFeature(Otc::GameProtocolChecksum))
@ -103,6 +115,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
send(msg); send(msg);
if(g_game.getProtocolVersion() >= 800)
enableXteaEncryption(); enableXteaEncryption();
} }