basic protocol 953 support, logging in real tibia!

This commit is contained in:
Eduardo Bart 2012-05-12 01:52:16 -03:00
parent abbd15b1c2
commit fa9c942471
18 changed files with 163 additions and 68 deletions

4
BUGS
View File

@ -2,6 +2,7 @@
modules recursivity makes client crash, it should generate a warning
== P1 BUGS (affects game play)
in some situations creatures may disappears while walking
sometimes minimap desync Z pos
follow and autowalk doesn't cancel when walking via hotkeys
when reading invalid spr/dat the client crashs
@ -27,5 +28,4 @@ hotkeys works while windows are locked, it shouldn't
skulls is rendering outside map bounds
party options does not work when re-logging inside a party
sometimes we can still view hits from above/bottom floors from a fight that is not visible
must removeThing in protocol parseTileAdd when stackpos is greater than 11
scroll does not follow characerlist selection when changing focus with up/down arrows

View File

@ -38,7 +38,7 @@ local function onContainerOpen(container, previousContainer)
name = name:sub(1,1):upper() .. name:sub(2)
containerWindow:setText(name)
containerItemWidget:setItemId(container:getItemId())
containerItemWidget:setItem(container:getContainerItem())
containerPanel:destroyChildren()
for slot=0,container:getCapacity()-1 do

View File

@ -223,6 +223,32 @@ uint64 FileStream::getU64()
return v;
}
std::string FileStream::getString()
{
std::string str;
int len = getU16();
if(len > 0 && len < 8192) {
char buffer[8192];
if(m_fileHandle) {
if(PHYSFS_read(m_fileHandle, buffer, 1, len) == 0)
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
else
str = std::string(buffer, len);
} else {
if(m_cacheReadPos+len > m_cacheBuffer.size()) {
logTraceError("operation failed on '", m_name, "': reached file eof");
return 0;
}
str = std::string((char*)&m_cacheBuffer[m_cacheReadPos], len);
m_cacheReadPos += len;
}
} else {
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
}
return str;
}
void FileStream::addU8(uint8 v)
{
if(PHYSFS_write(m_fileHandle, &v, 1, 1) != 1)

View File

@ -53,6 +53,7 @@ public:
uint16 getU16();
uint32 getU32();
uint64 getU64();
std::string getString();
void addU8(uint8 v);
void addU16(uint8 v);
void addU32(uint8 v);

View File

@ -124,9 +124,17 @@ void Protocol::internalRecvData(uint8* buffer, uint16 size)
return;
}
if(m_xteaEncryptionEnabled && !xteaDecrypt(m_inputMessage)) {
logTraceError("failed to decrypt message");
return;
if(m_xteaEncryptionEnabled) {
if(!xteaDecrypt(m_inputMessage)) {
logTraceError("failed to decrypt message");
return;
}
} else {
int size = m_inputMessage.getU16();
if(size != m_inputMessage.getUnreadSize()) {
logTraceError("invalid message size");
return;
}
}
onRecv(m_inputMessage);

View File

@ -6,9 +6,14 @@ ENDIF(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 6)
# otclient options
OPTION(BOT_PROTECTION "Enable bot protection" ON)
SET(PROTOCOL 860 CACHE "Protocol version" STRING)
SET(CIPSOFT_RSA "Use cipsoft RSA to login into original tibia" OFF)
ADD_DEFINITIONS(-DPROTOCOL=${PROTOCOL})
MESSAGE(STATUS "Protocol: " ${PROTOCOL})
IF(CIPSOFT_RSA)
ADD_DEFINITIONS(-DCIPSOFT_RSA)
ENDIF()
IF(BOT_PROTECTION)
ADD_DEFINITIONS(-DBOT_PROTECTION)
MESSAGE(STATUS "Bot protection: ON")

View File

@ -26,7 +26,6 @@
Container::Container()
{
m_id = -1;
m_itemId = 0;
m_capacity = 20;
m_name = "Container";
m_hasParent = false;

View File

@ -46,14 +46,14 @@ public:
void setId(int id) { m_id = id; }
void setCapacity(int capacity) { m_capacity = capacity; }
void setName(std::string name) { m_name = name; }
void setItemId(uint16 itemId) { m_itemId = itemId; }
void setContainerItem(const ItemPtr& containerItem) { m_containerItem = containerItem; }
void setHasParent(bool hasParent) { m_hasParent = hasParent; }
std::string getName() { return m_name; }
int getId() { return m_id; }
int getCapacity() { return m_capacity; }
int getItemsCount() { return m_items.size(); }
uint16 getItemId() { return m_itemId; }
ItemPtr getContainerItem() { return m_containerItem; }
bool hasParent() { return m_hasParent; }
private:
@ -61,7 +61,7 @@ private:
int m_id;
int m_capacity;
uint16 m_itemId;
ItemPtr m_containerItem;
std::string m_name;
bool m_hasParent;
std::deque<ItemPtr> m_items;

View File

@ -163,14 +163,14 @@ void Game::processCreatureSpeak(const std::string& name, int level, Otc::SpeakTy
g_lua.callGlobalField("g_game", "onCreatureSpeak", name, level, type, message, channelId, creaturePos);
}
void Game::processOpenContainer(int containerId, int itemId, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items)
void Game::processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items)
{
ContainerPtr previousContainer = getContainer(containerId);
ContainerPtr container = ContainerPtr(new Container());
container->setId(containerId);
container->setCapacity(capacity);
container->setName(name);
container->setItemId(itemId);
container->setContainerItem(containerItem);
container->setHasParent(hasParent);
m_containers[containerId] = container;
container->addItems(items);

View File

@ -64,7 +64,7 @@ protected:
void processCreatureSpeak(const std::string& name, int level, Otc::SpeakType type, const std::string& message, int channelId, const Position& creaturePos);
// container related
void processOpenContainer(int containerId, int itemId, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items);
void processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items);
void processCloseContainer(int containerId);
void processContainerAddItem(int containerId, const ItemPtr& item);
void processContainerUpdateItem(int containerId, int slot, const ItemPtr& item);

View File

@ -65,8 +65,9 @@ void ThingsType::parseThingType(const FileStreamPtr& fin, ThingType& thingType)
{
while(true) {
int property = fin->getU8();
if(property == ThingType::LastPropertyValue)
if(property == ThingType::LastPropertyValue) {
break;
}
thingType.m_properties[property] = true;
@ -93,6 +94,16 @@ void ThingsType::parseThingType(const FileStreamPtr& fin, ThingType& thingType)
#if PROTOCOL<=810
else if(property == ThingType::IsRune)
thingType.m_properties[ThingType::IsStackable] = true;
#endif
#if PROTOCOL>=944
else if(property == ThingType::Market) {
fin->getU16(); // category
fin->getU16(); // trade as
fin->getU16(); // show as
fin->getString(); // name
fin->getU16(); // restrict profession
fin->getU16(); // level
}
#endif
}

View File

@ -78,7 +78,7 @@ struct ThingType
IsFullGround,
IgnoreLook,
Cloth,
Animation,
Market,
LastProperty,
LastPropertyValue = 255
};

View File

@ -187,13 +187,13 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<Container>("setId", &Container::setId);
g_lua.bindClassMemberFunction<Container>("setCapacity", &Container::setCapacity);
g_lua.bindClassMemberFunction<Container>("setName", &Container::setName);
g_lua.bindClassMemberFunction<Container>("setItemId", &Container::setItemId);
g_lua.bindClassMemberFunction<Container>("setContainerItem", &Container::setContainerItem);
g_lua.bindClassMemberFunction<Container>("setHasParent", &Container::setHasParent);
g_lua.bindClassMemberFunction<Container>("getName", &Container::getName);
g_lua.bindClassMemberFunction<Container>("getId", &Container::getId);
g_lua.bindClassMemberFunction<Container>("getCapacity", &Container::getCapacity);
g_lua.bindClassMemberFunction<Container>("getItemsCount", &Container::getItemsCount);
g_lua.bindClassMemberFunction<Container>("getItemId", &Container::getItemId);
g_lua.bindClassMemberFunction<Container>("getContainerItem", &Container::getContainerItem);
g_lua.bindClassMemberFunction<Container>("hasParent", &Container::hasParent);
g_lua.registerClass<Thing>();

View File

@ -25,13 +25,11 @@
#include <otclient/global.h>
#if PROTOCOL != 810 && \
PROTOCOL != 854 && \
PROTOCOL != 860 && \
PROTOCOL != 861 && \
PROTOCOL != 862 && \
PROTOCOL != 870 && \
PROTOCOL != 910
#if !(PROTOCOL == 810) && \
!(PROTOCOL == 854) && \
!(PROTOCOL >= 860 && PROTOCOL <= 862) && \
!(PROTOCOL >= 870 && PROTOCOL <= 871) && \
!(PROTOCOL >= 910 && PROTOCOL <= 953)
#error "the supplied protocol version is not supported"
#endif
@ -50,8 +48,8 @@ namespace Proto {
"07119674283982419152118103759076030616683978566631413";
#endif
constexpr int PicSignature = 0x4F8C231A; // 953 pic signature
constexpr int ClientVersion = PROTOCOL;
constexpr int PicSignature = 0x4E119CBF;
enum OsTypes {
OsLinux = 1,
@ -89,7 +87,13 @@ namespace Proto {
GameServerLoginError = 20,
GameServerLoginAdvice = 21,
GameServerLoginWait = 22,
#if PROTOCOL>=953
GameServerPing = 29,
GameServerPingBack = 30,
#else
GameServerPingBack = 29,
GameServerPing = 30,
#endif
GameServerChallange = 31,
GameServerDeath = 40,
GameServerFullMap = 100,
@ -130,6 +134,7 @@ namespace Proto {
GameServerCreatureUnpass = 146,
GameServerEditText = 150,
GameServerEditList = 151,
GameServerPlayerDataBasic = 159,
GameServerPlayerData = 160,
GameServerPlayerSkills = 161,
GameServerPlayerState = 162,
@ -162,6 +167,14 @@ namespace Proto {
GameServerChannelEvent = 243,
GameServerObjectInfo = 244, // 910
GameServerPlayerInventory = 245, // 910
GameServerMarketEnter = 246, // 944
GameServerMarketLeave = 247, // 944
GameServerMarketBrowseItem = 248, // 944
GameServerMarketAcceptOffer = 249, // 944
GameServerMarketOwnOffers = 250, // 944
GameServerMarketCancelOffer = 251, // 944
GameServerMarketBrowseOwnHistory = 252, // 944
GameServerMarketMarketDetail = 253, // 944
GameServerExtendedOpcode = 254 // otclient only
};
@ -236,6 +249,11 @@ namespace Proto {
ClientRequestQuestLine = 241,
ClientRuleViolationReport = 242, // 910
ClientGetObjectInfo = 243, // 910
ClientMarketLeave = 244, // 944
ClientMarketBrowse = 245, // 944
ClientMarketCreateOffer = 246, // 944
ClientMarketCancelOffer = 247, // 944
ClientMarketAcceptOffer = 248, // 944
ClientExtendedOpcode = 254 // otclient only
};

View File

@ -42,37 +42,22 @@ void ProtocolGame::login(const std::string& accountName, const std::string& acco
void ProtocolGame::onConnect()
{
recv();
// must create local player before parsing anything
m_localPlayer = LocalPlayerPtr(new LocalPlayer);
#if PROTOCOL>=854
m_waitingLoginPacket = true;
enableChecksum();
#else
sendLoginPacket(0, 0);
#endif
recv();
}
void ProtocolGame::onRecv(InputMessage& inputMessage)
{
// only for protocol >= 860
if(m_waitingLoginPacket) {
inputMessage.skipBytes(3);
uint32 timestamp = inputMessage.getU32();
uint8 unknown = inputMessage.getU8();
m_waitingLoginPacket = false;
enableChecksum();
sendLoginPacket(timestamp, unknown);
recv();
}
else {
parseMessage(inputMessage);
recv();
}
parseMessage(inputMessage);
recv();
}
void ProtocolGame::onError(const boost::system::error_code& error)

View File

@ -103,7 +103,7 @@ public:
void sendExtendedOpcode(uint8 opcode, const std::string& buffer);
private:
void sendLoginPacket(uint timestamp, uint8 unknown);
void sendLoginPacket(uint challangeTimestamp, uint8 challangeRandom);
// Parse Messages
void parseMessage(InputMessage& msg);
@ -113,7 +113,8 @@ private:
void parseLoginError(InputMessage& msg);
void parseLoginAdvice(InputMessage& msg);
void parseLoginWait(InputMessage& msg);
void parsePing(InputMessage&);
void parsePing(InputMessage& msg);
void parseChallange(InputMessage& msg);
void parseDeath(InputMessage& msg);
void parseMapDescription(InputMessage& msg);
void parseMapMoveNorth(InputMessage& msg);
@ -150,6 +151,7 @@ private:
void parseCreatureTurn(InputMessage& msg);
void parseEditText(InputMessage& msg);
void parseEditList(InputMessage& msg);
void parsePlayerInfo(InputMessage& msg);
void parsePlayerStats(InputMessage& msg);
void parsePlayerSkills(InputMessage& msg);
void parsePlayerState(InputMessage& msg);
@ -198,8 +200,7 @@ private:
Position parsePosition(InputMessage& msg);
private:
Boolean<false> m_waitingLoginPacket;
Boolean<true> m_firstPacket;
Boolean<false> m_gameInitialized;
std::string m_accountName;
std::string m_accountPassword;
std::string m_characterName;

View File

@ -41,13 +41,9 @@ void ProtocolGame::parseMessage(InputMessage& msg)
try {
while(!msg.eof()) {
opcode = msg.getU8();
//dump << opcode;
if(m_firstPacket) {
if(opcode != Proto::GameServerInitGame)
logWarning("first server network opcode is not GameServerInitGame");
m_firstPacket = false;
}
if(!m_gameInitialized && opcode >= Proto::GameServerFullMap)
logWarning("first game network opcode is not GameServerInitGame");
switch(opcode) {
case Proto::GameServerInitGame:
@ -68,6 +64,12 @@ void ProtocolGame::parseMessage(InputMessage& msg)
case Proto::GameServerPing:
parsePing(msg);
break;
case Proto::GameServerPingBack:
// nothing todo
break;
case Proto::GameServerChallange:
parseChallange(msg);
break;
case Proto::GameServerDeath:
parseDeath(msg);
break;
@ -261,30 +263,44 @@ void ProtocolGame::parseMessage(InputMessage& msg)
parseQuestLine(msg);
break;
#if PROTOCOL>=870
case Proto::GameServerSpellDelay: // 870 only
case Proto::GameServerSpellDelay:
parseSpellDelay(msg);
break;
case Proto::GameServerSpellGroupDelay: // 870 only
case Proto::GameServerSpellGroupDelay:
parseSpellGroupDelay(msg);
break;
#endif
#if PROTOCOL>=910
case Proto::GameServerChannelEvent: // 910 only
case Proto::GameServerPlayerDataBasic:
parsePlayerInfo(msg);
break;
case Proto::GameServerChannelEvent:
parseChannelEvent(msg);
break;
case Proto::GameServerObjectInfo: // 910 only
case Proto::GameServerObjectInfo:
parseObjectInfo(msg);
break;
case Proto::GameServerPlayerInventory: // 910 only
case Proto::GameServerPlayerInventory:
parsePlayerInventory(msg);
break;
#endif
#if PROTOCOL>=944
case Proto::GameServerMarketEnter:
case Proto::GameServerMarketLeave:
case Proto::GameServerMarketBrowseItem:
case Proto::GameServerMarketAcceptOffer:
case Proto::GameServerMarketOwnOffers:
case Proto::GameServerMarketCancelOffer:
case Proto::GameServerMarketBrowseOwnHistory:
case Proto::GameServerMarketMarketDetail:
//TODO
break;
#endif
// additional opcode used by otclient only
case Proto::GameServerExtendedOpcode:
parseExtendedOpcode(msg);
break;
// not handled yet
//case Proto::GameServerChallange:
//case Proto::GameServerTrappers
//case Proto::GameServerWait:
default:
@ -300,6 +316,7 @@ void ProtocolGame::parseMessage(InputMessage& msg)
void ProtocolGame::parseInitGame(InputMessage& msg)
{
m_gameInitialized = true;
uint playerId = msg.getU32();
int serverBeat = msg.getU16();
bool canReportBugs = msg.getU8();
@ -338,12 +355,19 @@ void ProtocolGame::parseLoginWait(InputMessage& msg)
g_game.processLoginWait(message, time);
}
void ProtocolGame::parsePing(InputMessage&)
void ProtocolGame::parsePing(InputMessage& msg)
{
g_game.processPing();
sendPingResponse();
}
void ProtocolGame::parseChallange(InputMessage& msg)
{
uint32 timestamp = msg.getU32();
uint8 random = msg.getU8();
sendLoginPacket(timestamp, random);
}
void ProtocolGame::parseDeath(InputMessage& msg)
{
int penality = 100;
@ -474,7 +498,11 @@ void ProtocolGame::parseCreatureMove(InputMessage& msg)
void ProtocolGame::parseOpenContainer(InputMessage& msg)
{
int containerId = msg.getU8();
int itemId = msg.getU16();
#if PROTOCOL>=920
ItemPtr containerItem = internalGetItem(msg);
#else
ItemPtr containerItem = Item::create(msg.getU16());
#endif
std::string name = msg.getString();
int capacity = msg.getU8();
bool hasParent = (msg.getU8() != 0);
@ -484,7 +512,7 @@ void ProtocolGame::parseOpenContainer(InputMessage& msg)
for(int i = 0; i < itemCount; i++)
items[i] = internalGetItem(msg);
g_game.processOpenContainer(containerId, itemId, name, capacity, hasParent, items);
g_game.processOpenContainer(containerId, containerItem, name, capacity, hasParent, items);
}
void ProtocolGame::parseCloseContainer(InputMessage& msg)
@ -780,6 +808,16 @@ void ProtocolGame::parseEditList(InputMessage& msg)
g_game.processEditList(id, doorId, text);
}
void ProtocolGame::parsePlayerInfo(InputMessage& msg)
{
msg.getU8(); // is premium?
msg.getU8(); // profession
int numSpells = msg.getU16();
for(int i=0;i<numSpells;++i) {
msg.getU16(); // spell
}
}
void ProtocolGame::parsePlayerStats(InputMessage& msg)
{
double health = msg.getU16();

View File

@ -23,7 +23,7 @@
#include "protocolgame.h"
#include <framework/net/rsa.h>
void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown)
void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRandom)
{
OutputMessage msg;
@ -48,12 +48,15 @@ void ProtocolGame::sendLoginPacket(uint timestamp, uint8 unknown)
enableChecksum();
msg.addString(m_accountName);
paddingBytes -= 2 + m_accountName.length();
msg.addString(m_characterName);
paddingBytes -= 2 + m_characterName.length();
msg.addString(m_accountPassword);
paddingBytes -= 2 + m_accountPassword.length();
msg.addU32(timestamp);
msg.addU8(unknown);
paddingBytes -= 11 + m_accountName.length() + m_characterName.length() + m_accountPassword.length();
msg.addU32(challangeTimestamp);
msg.addU8(challangeRandom);
paddingBytes -= 5;
#else // PROTOCOL>=810
msg.addU32(Fw::fromstring<uint32>(m_accountName));
msg.addString(m_characterName);