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/data/creaturescripts/scripts/extendedopcode.lua b/data/creaturescripts/scripts/extendedopcode.lua new file mode 100644 index 0000000..c488a4d --- /dev/null +++ b/data/creaturescripts/scripts/extendedopcode.lua @@ -0,0 +1,13 @@ +OPCODE_LANGUAGE = 1 + +function onExtendedOpcode(cid, opcode, buffer) + if opcode == OPCODE_LANGUAGE then + -- otclient language + if buffer == 'en' or buffer == 'pt' then + -- example, setting player language, because otclient is multi-language... + --doCreatureSetStorage(cid, CREATURE_STORAGE_LANGUAGE, buffer) + end + else + -- other opcodes can be ignored, and the server will just work fine... + end +end 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(uint32_t playerId, uint8_t opcode, const std::string& buffer) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return; + + 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(uint32_t playerId, 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..7cb8313 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;} @@ -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..7a84f61 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; @@ -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->getID(), 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