diff --git a/src/otclient/game.cpp b/src/otclient/game.cpp index 524074a7..6bdb59f6 100644 --- a/src/otclient/game.cpp +++ b/src/otclient/game.cpp @@ -131,13 +131,14 @@ void Game::processGameStart() disableBotCall(); if(g_game.getFeature(Otc::GameClientPing)) { + m_protocolGame->sendPing(); m_pingEvent = g_dispatcher.cycleEvent([this] { if(m_protocolGame && m_protocolGame->isConnected()) { enableBotCall(); m_protocolGame->sendPing(); disableBotCall(); } - }, 1000); + }, 2000); } } diff --git a/src/otclient/protocolgame.h b/src/otclient/protocolgame.h index 2fbcd64b..31996282 100644 --- a/src/otclient/protocolgame.h +++ b/src/otclient/protocolgame.h @@ -222,7 +222,7 @@ private: std::string m_accountName; std::string m_accountPassword; std::string m_characterName; - Timer m_pingTimer; + stdext::timer m_pingTimer; LocalPlayerPtr m_localPlayer; }; diff --git a/src/otclient/protocolgameparse.cpp b/src/otclient/protocolgameparse.cpp index ac82d452..34765464 100644 --- a/src/otclient/protocolgameparse.cpp +++ b/src/otclient/protocolgameparse.cpp @@ -379,7 +379,7 @@ void ProtocolGame::parsePing(const InputMessagePtr& msg) void ProtocolGame::parsePingBack(const InputMessagePtr& msg) { - g_game.processPingBack(m_pingTimer.ticksElapsed()); + g_game.processPingBack(m_pingTimer.elapsed_millis()); } void ProtocolGame::parseChallange(const InputMessagePtr& msg) @@ -1379,7 +1379,7 @@ void ProtocolGame::parseExtendedOpcode(const InputMessagePtr& msg) if(opcode == 0) m_enableSendExtendedOpcode = true; else - callLuaField("onExtendedOpcode", opcode, buffer); + callLuaField("onExtendedOpcode", opcode, buffer); } void ProtocolGame::setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height) diff --git a/tools/tfs_extendedopcode.patch b/tools/tfs_extendedopcode.patch new file mode 100644 index 00000000..b9054951 --- /dev/null +++ b/tools/tfs_extendedopcode.patch @@ -0,0 +1,396 @@ +diff --git a/const.h b/const.h +index cd479e4..8d46b23 100644 +--- a/const.h ++++ b/const.h +@@ -21,9 +21,12 @@ + + enum OperatingSystem_t + { +- CLIENTOS_LINUX = 0x01, +- CLIENTOS_WINDOWS = 0x02, +- CLIENTOS_FLASH = 0x03 ++ CLIENTOS_LINUX = 0x01, ++ CLIENTOS_WINDOWS = 0x02, ++ CLIENTOS_FLASH = 0x03, ++ CLIENTOS_OTCLIENT_LINUX = 0x0A, ++ CLIENTOS_OTCLIENT_WINDOWS = 0x0B, ++ CLIENTOS_OTCLIENT_MAC = 0x0C, + }; + + enum ChannelEvent_t +diff --git a/creatureevent.cpp b/creatureevent.cpp +index 842b237..e9b2200 100644 +--- a/creatureevent.cpp ++++ b/creatureevent.cpp +@@ -211,6 +211,8 @@ CreatureEventType_t CreatureEvents::getType(const std::string& type) + _type = CREATURE_EVENT_DEATH; + else if(type == "preparedeath") + _type = CREATURE_EVENT_PREPAREDEATH; ++ else if(type == "extendedopcode") ++ _type = CREATURE_EVENT_EXTENDED_OPCODE; + + return _type; + } +@@ -330,6 +332,8 @@ std::string CreatureEvent::getScriptEventName() const + return "onDeath"; + case CREATURE_EVENT_PREPAREDEATH: + return "onPrepareDeath"; ++ case CREATURE_EVENT_EXTENDED_OPCODE: ++ return "onExtendedOpcode"; + case CREATURE_EVENT_NONE: + default: + break; +@@ -401,6 +405,8 @@ std::string CreatureEvent::getScriptEventParams() const + return "cid, corpse, deathList"; + case CREATURE_EVENT_PREPAREDEATH: + return "cid, deathList"; ++ case CREATURE_EVENT_EXTENDED_OPCODE: ++ return "cid, opcode, buffer"; + case CREATURE_EVENT_NONE: + default: + break; +@@ -2145,3 +2151,57 @@ uint32_t CreatureEvent::executeAction(Creature* creature, Creature* target) + return 0; + } + } ++ ++uint32_t CreatureEvent::executeExtendedOpcode(Creature* creature, uint8_t opcode, const std::string& buffer) ++{ ++ //onExtendedOpcode(cid, opcode, buffer) ++ if(m_interface->reserveEnv()) ++ { ++ ScriptEnviroment* env = m_interface->getEnv(); ++ if(m_scripted == EVENT_SCRIPT_BUFFER) ++ { ++ env->setRealPos(creature->getPosition()); ++ std::stringstream scriptstream; ++ scriptstream << "local cid = " << env->addThing(creature) << std::endl; ++ scriptstream << "local opcode = " << (int)opcode << std::endl; ++ scriptstream << "local buffer = " << buffer.c_str() << std::endl; ++ ++ scriptstream << m_scriptData; ++ bool result = true; ++ if(m_interface->loadBuffer(scriptstream.str())) ++ { ++ lua_State* L = m_interface->getState(); ++ result = m_interface->getGlobalBool(L, "_result", true); ++ } ++ ++ m_interface->releaseEnv(); ++ return result; ++ } ++ else ++ { ++ #ifdef __DEBUG_LUASCRIPTS__ ++ char desc[35]; ++ sprintf(desc, "%s", player->getName().c_str()); ++ env->setEvent(desc); ++ #endif ++ ++ env->setScriptId(m_scriptId, m_interface); ++ env->setRealPos(creature->getPosition()); ++ ++ lua_State* L = m_interface->getState(); ++ m_interface->pushFunction(m_scriptId); ++ lua_pushnumber(L, env->addThing(creature)); ++ lua_pushnumber(L, opcode); ++ lua_pushlstring(L, buffer.c_str(), buffer.length()); ++ ++ bool result = m_interface->callFunction(3); ++ m_interface->releaseEnv(); ++ return result; ++ } ++ } ++ else ++ { ++ std::cout << "[Error - CreatureEvent::executeRemoved] Call stack overflow." << std::endl; ++ return 0; ++ } ++} +diff --git a/creatureevent.h b/creatureevent.h +index f1ff4b2..cc5171c 100644 +--- a/creatureevent.h ++++ b/creatureevent.h +@@ -57,7 +57,8 @@ enum CreatureEventType_t + CREATURE_EVENT_CAST, + CREATURE_EVENT_KILL, + CREATURE_EVENT_DEATH, +- CREATURE_EVENT_PREPAREDEATH ++ CREATURE_EVENT_PREPAREDEATH, ++ CREATURE_EVENT_EXTENDED_OPCODE // otclient additional network opcodes + }; + + enum StatsChange_t +@@ -150,6 +151,7 @@ class CreatureEvent : public Event + uint32_t executeKill(Creature* creature, Creature* target, const DeathEntry& entry); + uint32_t executeDeath(Creature* creature, Item* corpse, DeathList deathList); + uint32_t executePrepareDeath(Creature* creature, DeathList deathList); ++ uint32_t executeExtendedOpcode(Creature* creature, uint8_t opcode, const std::string& buffer); + // + + protected: +diff --git a/data/creaturescripts/creaturescripts.xml b/data/creaturescripts/creaturescripts.xml +index 363c62b..c706f10 100644 +--- a/data/creaturescripts/creaturescripts.xml ++++ b/data/creaturescripts/creaturescripts.xml +@@ -14,4 +14,6 @@ + + + ++ ++ + +diff --git a/game.cpp b/game.cpp +index 2e4dc2c..7508591 100644 +--- a/game.cpp ++++ b/game.cpp +@@ -6951,3 +6951,12 @@ void Game::checkExpiredMarketOffers() + + Scheduler::getInstance().addEvent(createSchedulerTask(checkExpiredMarketOffersEachMinutes * 60 * 1000, boost::bind(&Game::checkExpiredMarketOffers, this))); + } ++ ++void Game::parsePlayerExtendedOpcode(Player *player, uint8_t opcode, const std::string& buffer) ++{ ++ if(player) { ++ CreatureEventList extendedOpcodeEvents = player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE); ++ for(CreatureEventList::iterator it = extendedOpcodeEvents.begin(); it != extendedOpcodeEvents.end(); ++it) ++ (*it)->executeExtendedOpcode(player, opcode, buffer); ++ } ++} +diff --git a/game.h b/game.h +index 51fa397..7192549 100644 +--- a/game.h ++++ b/game.h +@@ -646,6 +646,8 @@ class Game + std::map grounds; + #endif + ++ void parsePlayerExtendedOpcode(Player *player, uint8_t opcode, const std::string& buffer); ++ + protected: + bool playerWhisper(Player* player, const std::string& text, uint32_t statementId); + bool playerYell(Player* player, const std::string& text, uint32_t statementId); +diff --git a/luascript.cpp b/luascript.cpp +index 4cb8c8d..4ed9391 100644 +--- a/luascript.cpp ++++ b/luascript.cpp +@@ -2476,6 +2476,12 @@ void LuaInterface::registerFunctions() + //getConfigFile() + lua_register(m_luaState, "getConfigFile", LuaInterface::luaGetConfigFile); + ++ //isPlayerUsingOtclient(cid) ++ lua_register(m_luaState, "isPlayerUsingOtclient", LuaInterface::luaIsPlayerUsingOtclient); ++ ++ //doSendPlayerExtendedOpcode(cid, opcode, buffer) ++ lua_register(m_luaState, "doSendPlayerExtendedOpcode", LuaInterface::luaDoSendPlayerExtendedOpcode); ++ + //getConfigValue(key) + lua_register(m_luaState, "getConfigValue", LuaInterface::luaGetConfigValue); + +@@ -9471,6 +9477,32 @@ int32_t LuaInterface::luaGetMountInfo(lua_State* L) + return 1; + } + ++int32_t LuaInterface::luaIsPlayerUsingOtclient(lua_State* L) ++{ ++ //isPlayerUsingOtclient(cid) ++ ScriptEnviroment* env = getEnv(); ++ if(Player* player = env->getPlayerByUID(popNumber(L))) { ++ lua_pushboolean(L, player->isUsingOtclient()); ++ } ++ lua_pushboolean(L, false); ++ return 1; ++} ++ ++int32_t LuaInterface::luaDoSendPlayerExtendedOpcode(lua_State* L) ++{ ++ //doSendPlayerExtendedOpcode(cid, opcode, buffer) ++ std::string buffer = popString(L); ++ int opcode = popNumber(L); ++ ++ ScriptEnviroment* env = getEnv(); ++ if(Player* player = env->getPlayerByUID(popNumber(L))) { ++ player->sendExtendedOpcode(opcode, buffer); ++ lua_pushboolean(L, true); ++ } ++ lua_pushboolean(L, false); ++ return 1; ++} ++ + int32_t LuaInterface::luaGetPartyMembers(lua_State* L) + { + //getPartyMembers(cid) +diff --git a/luascript.h b/luascript.h +index 234091a..0a0046f 100644 +--- a/luascript.h ++++ b/luascript.h +@@ -697,6 +697,9 @@ class LuaInterface + static int32_t luaDoPlayerSetMounted(lua_State* L); + static int32_t luaGetMountInfo(lua_State* L); + ++ static int32_t luaIsPlayerUsingOtclient(lua_State* L); ++ static int32_t luaDoSendPlayerExtendedOpcode(lua_State* L); ++ + static int32_t luaL_errors(lua_State* L); + static int32_t luaL_loadmodlib(lua_State* L); + static int32_t luaL_domodlib(lua_State* L); +diff --git a/networkmessage.cpp b/networkmessage.cpp +index 917e36a..3671750 100644 +--- a/networkmessage.cpp ++++ b/networkmessage.cpp +@@ -171,16 +171,16 @@ Position NetworkMessage::getPosition() + return pos; + } + +-void NetworkMessage::putString(const char* value, bool addSize/* = true*/) ++void NetworkMessage::putString(const char* value, int length, bool addSize/* = true*/) + { +- uint32_t size = (uint32_t)strlen(value); ++ uint32_t size = (uint32_t)length; + if(!hasSpace(size + (addSize ? 2 : 0)) || size > 8192) + return; + + if(addSize) + put(size); + +- strcpy((char*)(m_buffer + m_position), value); ++ memcpy((char*)(m_buffer + m_position), value, length); + m_position += size; + m_size += size; + } +diff --git a/networkmessage.h b/networkmessage.h +index 6cf8ee1..615f094 100644 +--- a/networkmessage.h ++++ b/networkmessage.h +@@ -80,8 +80,8 @@ class NetworkMessage + m_size += sizeof(T); + } + +- void putString(const std::string& value, bool addSize = true) {putString(value.c_str(), addSize);} +- void putString(const char* value, bool addSize = true); ++ void putString(const std::string& value, bool addSize = true) {putString(value.c_str(), value.length(), addSize);} ++ void putString(const char* value, int length, bool addSize = true); + + void putPadding(uint32_t amount); + +diff --git a/player.h b/player.h +index 63e9183..e003e63 100644 +--- a/player.h ++++ b/player.h +@@ -228,6 +228,7 @@ class Player : public Creature, public Cylinder + bool hasPVPBlessing() const {return pvpBlessing;} + uint16_t getBlessings() const; + ++ bool isUsingOtclient() const { return operatingSystem >= CLIENTOS_OTCLIENT_LINUX; } + OperatingSystem_t getOperatingSystem() const {return operatingSystem;} + void setOperatingSystem(OperatingSystem_t os) {operatingSystem = os;} + uint32_t getClientVersion() const {return clientVersion;} +@@ -400,7 +401,7 @@ class Player : public Creature, public Cylinder + onSell = saleCallback; + return shopOwner; + } +- ++ + //Quest functions + void onUpdateQuest(); + +@@ -580,6 +581,9 @@ class Player : public Creature, public Cylinder + void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t cooldown) + {if(client) client->sendSpellGroupCooldown(groupId, cooldown);} + ++ void sendExtendedOpcode(uint8_t opcode, const std::string& buffer) ++ {if(client) client->sendExtendedOpcode(opcode, buffer);} ++ + //container + void sendAddContainerItem(const Container* container, const Item* item); + void sendUpdateContainerItem(const Container* container, uint8_t slot, const Item* oldItem, const Item* newItem); +diff --git a/protocolgame.cpp b/protocolgame.cpp +index b980be0..c75654e 100644 +--- a/protocolgame.cpp ++++ b/protocolgame.cpp +@@ -263,6 +263,11 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string + return false; + } + ++ if(player->isUsingOtclient()) ++ { ++ player->registerCreatureEvent("ExtendedOpcode"); ++ } ++ + player->lastIP = player->getIP(); + player->lastLoad = OTSYS_TIME(); + player->lastLogin = std::max(time(NULL), player->lastLogin + 1); +@@ -427,6 +432,10 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) + enableXTEAEncryption(); + setXTEAKey(key); + ++ // notifies to otclient that this server can receive extended game protocol opcodes ++ if(operatingSystem >= CLIENTOS_OTCLIENT_LINUX) ++ sendExtendedOpcode(0x00, std::string()); ++ + bool gamemaster = (msg.get() != (char)0); + std::string name = msg.getString(), character = msg.getString(), password = msg.getString(); + +@@ -578,6 +587,10 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) + parseReceivePing(msg); + break; + ++ case 0x32: // otclient extended opcode ++ parseExtendedOpcode(msg); ++ break; ++ + case 0x64: // move with steps + parseAutoWalk(msg); + break; +@@ -2411,7 +2424,7 @@ void ProtocolGame::sendMarketDetail(uint16_t itemId) + } + else + msg->put(0x00); +- ++ + if((statistics = IOMarket::getInstance()->getSaleStatistics(itemId))) + { + msg->put(0x01); +@@ -3705,3 +3718,28 @@ void ProtocolGame::AddShopItem(NetworkMessage_ptr msg, const ShopInfo& item) + msg->put(item.buyPrice); + msg->put(item.sellPrice); + } ++ ++void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg) ++{ ++ uint8_t opcode = msg.get(); ++ std::string buffer = msg.getString(); ++ ++ // process additional opcodes via lua script event ++ addGameTask(&Game::parsePlayerExtendedOpcode, player, opcode, buffer); ++} ++ ++void ProtocolGame::sendExtendedOpcode(uint8_t opcode, const std::string& buffer) ++{ ++ // extended opcodes can only be send to players using otclient, cipsoft's tibia can't understand them ++ if(player && !player->isUsingOtclient()) ++ return; ++ ++ NetworkMessage_ptr msg = getOutputBuffer(); ++ if(msg) ++ { ++ TRACK_MESSAGE(msg); ++ msg->put(0x32); ++ msg->put(opcode); ++ msg->putString(buffer); ++ } ++} +diff --git a/protocolgame.h b/protocolgame.h +index 7691174..48b9bf1 100644 +--- a/protocolgame.h ++++ b/protocolgame.h +@@ -326,6 +326,9 @@ class ProtocolGame : public Protocol + //shop + void AddShopItem(NetworkMessage_ptr msg, const ShopInfo& item); + ++ void parseExtendedOpcode(NetworkMessage& msg); ++ void sendExtendedOpcode(uint8_t opcode, const std::string& buffer); ++ + #define addGameTask(f, ...) addGameTaskInternal(0, boost::bind(f, &g_game, __VA_ARGS__)) + #define addGameTaskTimed(delay, f, ...) addGameTaskInternal(delay, boost::bind(f, &g_game, __VA_ARGS__)) + template