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)
end
elseif hotKey.useType == HOTKEY_MANAGER_USE then
g_game.useInventoryItem(hotKey.itemId)
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)
end
elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then
g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer())
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())
end
elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then
local attackingCreature = g_game.getAttackingCreature()
if not attackingCreature then return end
g_game.useInventoryItemWith(hotKey.itemId, attackingCreature)
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)
end
elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then
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)
end
end

View File

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

View File

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

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()