Created
August 24, 2019 19:20
-
-
Save lyuz1n/62bd5e635cb1d0fade73acc7cfa063b4 to your computer and use it in GitHub Desktop.
Cam System 10.98
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "otpch.h" | |
| #include "protocolreplay.h" | |
| #include "outputmessage.h" | |
| #include "configmanager.h" | |
| #include "game.h" | |
| #include "ban.h" | |
| #include "scheduler.h" | |
| extern ConfigManager g_config; | |
| extern Game g_game; | |
| extern ZReplays g_replays; | |
| void ProtocolReplay::release() | |
| { | |
| OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this()); | |
| Protocol::release(); | |
| } | |
| void ProtocolReplay::onRecvFirstMessage(NetworkMessage& msg) | |
| { | |
| if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { | |
| disconnect(); | |
| return; | |
| } | |
| OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>()); | |
| uint16_t version = msg.get<uint16_t>(); | |
| msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision | |
| if (!Protocol::RSA_decrypt(msg)) { | |
| disconnect(); | |
| return; | |
| } | |
| xtea::key key; | |
| key[0] = msg.get<uint32_t>(); | |
| key[1] = msg.get<uint32_t>(); | |
| key[2] = msg.get<uint32_t>(); | |
| key[3] = msg.get<uint32_t>(); | |
| enableXTEAEncryption(); | |
| setXTEAKey(std::move(key)); | |
| if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { | |
| NetworkMessage opcodeMessage; | |
| opcodeMessage.addByte(0x32); | |
| opcodeMessage.addByte(0x00); | |
| opcodeMessage.add<uint16_t>(0x00); | |
| writeToOutputBuffer(opcodeMessage); | |
| } | |
| msg.skipBytes(1); // gamemaster flag | |
| std::string sessionKey = msg.getString(); | |
| std::string characterName = msg.getString(); | |
| uint32_t timeStamp = msg.get<uint32_t>(); | |
| uint8_t randNumber = msg.getByte(); | |
| if (challengeTimestamp != timeStamp || challengeRandom != randNumber) { | |
| disconnect(); | |
| return; | |
| } | |
| g_scheduler.addEvent(createSchedulerTask(0, std::bind(&ProtocolReplay::watchRecording, getThis(), characterName))); | |
| } | |
| void ProtocolReplay::onConnect() | |
| { | |
| auto output = OutputMessagePool::getOutputMessage(); | |
| static std::random_device rd; | |
| static std::ranlux24 generator(rd()); | |
| static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF); | |
| // Skip checksum | |
| output->skipBytes(sizeof(uint32_t)); | |
| // Packet length & type | |
| output->add<uint16_t>(0x0006); | |
| output->addByte(0x1F); | |
| // Add timestamp & random number | |
| challengeTimestamp = static_cast<uint32_t>(time(nullptr)); | |
| output->add<uint32_t>(challengeTimestamp); | |
| challengeRandom = randNumber(generator); | |
| output->addByte(challengeRandom); | |
| // Go back and write checksum | |
| output->skipBytes(-12); | |
| output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8)); | |
| send(output); | |
| } | |
| void ProtocolReplay::disconnectClient(const std::string& message) const | |
| { | |
| auto output = OutputMessagePool::getOutputMessage(); | |
| output->addByte(0x14); | |
| output->addString(message); | |
| send(output); | |
| disconnect(); | |
| } | |
| void ProtocolReplay::writeToOutputBuffer(const NetworkMessage& msg) | |
| { | |
| auto out = getOutputBuffer(msg.getLength()); | |
| out->append(msg); | |
| } | |
| void ProtocolReplay::watchRecording(const std::string& replayName) | |
| { | |
| for (const auto& replay : g_replays.getReplays()) { | |
| if (replay.title == replayName) { | |
| data = replay.packets; | |
| break; | |
| } | |
| } | |
| if (data.empty()) { | |
| disconnectClient("This replay doesn't exists."); | |
| return; | |
| } | |
| playRecording(); | |
| OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this()); | |
| } | |
| void ProtocolReplay::playRecording() | |
| { | |
| if (data.empty()) { | |
| releaseEvent(); | |
| return; | |
| } | |
| const NetworkMessage& msg = data.front().second; | |
| time_t interval = data.front().first; | |
| data.pop(); | |
| if (data.empty()) { | |
| releaseEvent(); | |
| return; | |
| } | |
| interval = data.front().first - interval; | |
| eventId = g_scheduler.addEvent(createSchedulerTask(interval, std::bind(&ProtocolReplay::playRecording, getThis()))); | |
| writeToOutputBuffer(msg); | |
| } | |
| void ProtocolReplay::parsePacket(NetworkMessage& msg) | |
| { | |
| if (msg.getLength() <= 0) { | |
| return; | |
| } | |
| uint8_t recvbyte = msg.getByte(); | |
| switch (recvbyte) { | |
| case 0x14: releaseEvent(); break; | |
| default: | |
| NetworkMessage msg; | |
| msg.addByte(0xB5); | |
| msg.addByte(2); | |
| writeToOutputBuffer(msg); | |
| } | |
| if (msg.isOverrun()) { | |
| disconnect(); | |
| } | |
| } | |
| void ProtocolReplay::releaseEvent() | |
| { | |
| if (eventId) { | |
| g_scheduler.stopEvent(eventId); | |
| eventId = 0; | |
| } | |
| disconnect(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef __PROTOCOLREPLAY__ | |
| #define __PROTOCOLREPLAY__ | |
| #include "protocol.h" | |
| #include "z_replay.h" | |
| class ProtocolReplay; | |
| using ProtocolReplay_ptr = std::shared_ptr<ProtocolReplay>; | |
| class ProtocolReplay final : public Protocol | |
| { | |
| public: | |
| enum {server_sends_first = true}; | |
| enum {protocol_identifier = 0}; | |
| enum {use_checksum = true}; | |
| static const char* protocol_name() { | |
| return "replay protocol"; | |
| } | |
| explicit ProtocolReplay(Connection_ptr connection) : Protocol(connection) {} | |
| private: | |
| ProtocolReplay_ptr getThis() { | |
| return std::static_pointer_cast<ProtocolReplay>(shared_from_this()); | |
| } | |
| void writeToOutputBuffer(const NetworkMessage& msg); | |
| void release() override; | |
| void releaseEvent(); | |
| void disconnectClient(const std::string& message) const; | |
| void parsePacket(NetworkMessage& msg) override; | |
| void onRecvFirstMessage(NetworkMessage& msg) override; | |
| void onConnect() override; | |
| void watchRecording(const std::string& replayName); | |
| void playRecording(); | |
| PacketQueue data; | |
| uint32_t eventId = 0; | |
| uint32_t challengeTimestamp = 0; | |
| uint8_t challengeRandom = 0; | |
| }; | |
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- C:\Users\halls\Desktop\forgottenserver\src\configmanager.cpp 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\configmanager.cpp 2019-08-24 16:13:17.053000000 -0300 | |
| @@ -116,7 +116,7 @@ | |
| integer[GAME_PORT] = getGlobalNumber(L, "gameProtocolPort", 7172); | |
| integer[LOGIN_PORT] = getGlobalNumber(L, "loginProtocolPort", 7171); | |
| integer[STATUS_PORT] = getGlobalNumber(L, "statusProtocolPort", 7171); | |
| - | |
| + integer[REPLAY_PORT] = getGlobalNumber(L, "replayProtocolPort", 7174); | |
| integer[MARKET_OFFER_DURATION] = getGlobalNumber(L, "marketOfferDuration", 30 * 24 * 60 * 60); | |
| } | |
| --- C:\Users\halls\Desktop\forgottenserver\src\configmanager.h 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\configmanager.h 2019-08-22 20:05:20.817000000 -0300 | |
| @@ -94,6 +94,7 @@ | |
| GAME_PORT, | |
| LOGIN_PORT, | |
| STATUS_PORT, | |
| + REPLAY_PORT, | |
| STAIRHOP_DELAY, | |
| MARKET_OFFER_DURATION, | |
| CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES, | |
| --- C:\Users\halls\Desktop\forgottenserver\src\luascript.cpp 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\luascript.cpp 2019-08-24 15:52:59.672000000 -0300 | |
| @@ -53,6 +53,7 @@ | |
| extern GlobalEvents* g_globalEvents; | |
| extern Scripts* g_scripts; | |
| extern Weapons* g_weapons; | |
| +extern ZReplays g_replays; | |
| ScriptEnvironment::DBResultMap ScriptEnvironment::tempResults; | |
| uint32_t ScriptEnvironment::lastResultId = 0; | |
| @@ -2429,6 +2430,10 @@ | |
| registerMethod("Player", "hasSecureMode", LuaScriptInterface::luaPlayerHasSecureMode); | |
| registerMethod("Player", "getFightMode", LuaScriptInterface::luaPlayerGetFightMode); | |
| + // Replay | |
| + registerMethod("Player", "startReplay", LuaScriptInterface::luaPlayerStartReplay); | |
| + registerMethod("Player", "stopReplay", LuaScriptInterface::luaPlayerStopReplay); | |
| + | |
| // Monster | |
| registerClass("Monster", "Creature", LuaScriptInterface::luaMonsterCreate); | |
| registerMetaMethod("Monster", "__eq", LuaScriptInterface::luaUserdataCompare); | |
| @@ -16159,3 +16164,39 @@ | |
| luaL_unref(luaState, LUA_REGISTRYINDEX, parameter); | |
| } | |
| } | |
| + | |
| +int LuaScriptInterface::luaPlayerStartReplay(lua_State* L) | |
| +{ | |
| + // player:startReplay() | |
| + Player* player = getUserdata<Player>(L, 1); | |
| + if (player) { | |
| + if (player->replayData.recording) { | |
| + pushBoolean(L, false); | |
| + } else { | |
| + player->startReplay(); | |
| + pushBoolean(L, true); | |
| + } | |
| + } else { | |
| + lua_pushnil(L); | |
| + } | |
| + | |
| + return 1; | |
| +} | |
| + | |
| +int LuaScriptInterface::luaPlayerStopReplay(lua_State* L) | |
| +{ | |
| + // player:stopReplay() | |
| + Player* player = getUserdata<Player>(L, 1); | |
| + if (player) { | |
| + if (!player->replayData.recording) { | |
| + pushBoolean(L, false); | |
| + } else { | |
| + g_replays.saveReplay(player->replayData); | |
| + pushBoolean(L, true); | |
| + } | |
| + } else { | |
| + lua_pushnil(L); | |
| + } | |
| + | |
| + return 1; | |
| +} | |
| --- C:\Users\halls\Desktop\forgottenserver\src\luascript.h 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\luascript.h 2019-08-22 18:20:21.985000000 -0300 | |
| @@ -992,6 +992,10 @@ | |
| static int luaPlayerHasSecureMode(lua_State* L); | |
| static int luaPlayerGetFightMode(lua_State* L); | |
| + // Replay | |
| + static int luaPlayerStartReplay(lua_State* L); | |
| + static int luaPlayerStopReplay(lua_State* L); | |
| + | |
| // Monster | |
| static int luaMonsterCreate(lua_State* L); | |
| --- C:\Users\halls\Desktop\forgottenserver\src\otserv.cpp 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\otserv.cpp 2019-08-24 15:56:38.867000000 -0300 | |
| @@ -31,6 +31,7 @@ | |
| #include "protocolold.h" | |
| #include "protocollogin.h" | |
| #include "protocolstatus.h" | |
| +#include "protocolreplay.h" | |
| #include "databasemanager.h" | |
| #include "scheduler.h" | |
| #include "databasetasks.h" | |
| @@ -48,6 +49,8 @@ | |
| extern Scripts* g_scripts; | |
| RSA g_RSA; | |
| +ZReplays g_replays; | |
| + | |
| std::mutex g_loaderLock; | |
| std::condition_variable g_loaderSignal; | |
| std::unique_lock<std::mutex> g_loaderUniqueLock(g_loaderLock); | |
| @@ -269,6 +272,9 @@ | |
| services->add<ProtocolGame>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::GAME_PORT))); | |
| services->add<ProtocolLogin>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT))); | |
| + // Replay | |
| + services->add<ProtocolReplay>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::REPLAY_PORT))); | |
| + | |
| // OT protocols | |
| services->add<ProtocolStatus>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STATUS_PORT))); | |
| --- C:\Users\halls\Desktop\forgottenserver\src\player.h 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\player.h 2019-08-22 21:33:45.034000000 -0300 | |
| @@ -36,6 +36,7 @@ | |
| #include "groups.h" | |
| #include "town.h" | |
| #include "mounts.h" | |
| +#include "z_replay.h" | |
| class House; | |
| class NetworkMessage; | |
| @@ -1142,6 +1143,12 @@ | |
| void forgetInstantSpell(const std::string& spellName); | |
| bool hasLearnedInstantSpell(const std::string& spellName) const; | |
| + void startReplay() { | |
| + if (client) { | |
| + client->startReplay(); | |
| + } | |
| + } | |
| + | |
| private: | |
| std::forward_list<Condition*> getMuteConditions() const; | |
| @@ -1215,6 +1222,8 @@ | |
| Position loginPosition; | |
| Position lastWalkthroughPosition; | |
| + ZReplayData replayData; | |
| + | |
| time_t lastLoginSaved = 0; | |
| time_t lastLogout = 0; | |
| --- C:\Users\halls\Desktop\forgottenserver\src\protocolgame.cpp 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\protocolgame.cpp 2019-08-22 22:46:21.443000000 -0300 | |
| @@ -40,11 +40,16 @@ | |
| extern Actions actions; | |
| extern CreatureEvents* g_creatureEvents; | |
| extern Chat* g_chat; | |
| +extern ZReplays g_replays; | |
| void ProtocolGame::release() | |
| { | |
| //dispatcher thread | |
| if (player && player->client == shared_from_this()) { | |
| + if (player->replayData.recording) { | |
| + g_replays.saveReplay(player->replayData); | |
| + } | |
| + | |
| player->client.reset(); | |
| player->decrementReferenceCounter(); | |
| player = nullptr; | |
| @@ -385,6 +390,11 @@ | |
| { | |
| auto out = getOutputBuffer(msg.getLength()); | |
| out->append(msg); | |
| + | |
| + // add packets to data | |
| + if (player->replayData.recording) { | |
| + player->replayData.packets.push({ OTSYS_TIME(), msg }); | |
| + } | |
| } | |
| void ProtocolGame::parsePacket(NetworkMessage& msg) | |
| @@ -3089,3 +3099,71 @@ | |
| // process additional opcodes via lua script event | |
| addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer); | |
| } | |
| + | |
| +void ProtocolGame::startReplay() | |
| +{ | |
| + if (g_replays.getReplays().size() >= Z_MAX_REPLAYS) { | |
| + return; | |
| + } | |
| + | |
| + knownCreatureSet.clear(); | |
| + player->replayData.recording = true; | |
| + | |
| + // Let's go to simulate a sendCreature | |
| + NetworkMessage msg; | |
| + msg.addByte(0x17); | |
| + msg.add<uint32_t>(player->getID()); | |
| + msg.add<uint16_t>(0x32); | |
| + msg.addDouble(Creature::speedA, 3); | |
| + msg.addDouble(Creature::speedB, 3); | |
| + msg.addDouble(Creature::speedC, 3); | |
| + msg.addByte(0x00); | |
| + msg.addByte(0x00); | |
| + msg.addByte(0x00); | |
| + msg.add<uint16_t>(0x00); | |
| + msg.add<uint16_t>(25); | |
| + msg.addByte(0x0A); | |
| + msg.addByte(0x0F); | |
| + | |
| + const Position& pos = player->getPosition(); | |
| + msg.addByte(0x64); | |
| + msg.addPosition(player->getPosition()); | |
| + GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg); | |
| + | |
| + for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { | |
| + const Item* item = player->getInventoryItem(static_cast<slots_t>(i)); | |
| + if (item) { | |
| + msg.addByte(0x78); | |
| + msg.addByte(static_cast<slots_t>(i)); | |
| + msg.addItem(item); | |
| + } else { | |
| + msg.addByte(0x79); | |
| + msg.addByte(static_cast<slots_t>(i)); | |
| + } | |
| + } | |
| + | |
| + AddPlayerStats(msg); | |
| + AddPlayerSkills(msg); | |
| + AddWorldLight(msg, g_game.getWorldLightInfo()); | |
| + if (canSee(player)) { | |
| + AddCreatureLight(msg, player); | |
| + } | |
| + | |
| + sendVIPEntries(); | |
| + msg.addByte(0x9F); | |
| + if (player->isPremium()) { | |
| + msg.addByte(1); | |
| + msg.add<uint32_t>(time(nullptr) + (player->premiumDays * 86400)); | |
| + } else { | |
| + msg.addByte(0); | |
| + msg.add<uint32_t>(0); | |
| + } | |
| + msg.addByte(player->getVocation()->getClientId()); | |
| + msg.add<uint16_t>(0xFF); | |
| + for (uint8_t spellId = 0x00; spellId < 0xFF; spellId++) { | |
| + msg.addByte(spellId); | |
| + } | |
| + | |
| + player->sendIcons(); | |
| + player->replayData.packets.push({ OTSYS_TIME(), msg }); | |
| +} | |
| --- C:\Users\halls\Desktop\forgottenserver\src\protocolgame.h 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\protocolgame.h 2019-08-22 19:37:15.078000000 -0300 | |
| @@ -301,6 +301,8 @@ | |
| //otclient | |
| void parseExtendedOpcode(NetworkMessage& msg); | |
| + void startReplay(); | |
| + | |
| friend class Player; | |
| // Helpers so we don't need to bind every time | |
| --- C:\Users\halls\Desktop\forgottenserver\src\protocollogin.cpp 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\protocollogin.cpp 2019-08-22 22:08:18.459000000 -0300 | |
| @@ -31,6 +31,7 @@ | |
| extern ConfigManager g_config; | |
| extern Game g_game; | |
| +extern ZReplays g_replays; | |
| void ProtocolLogin::disconnectClient(const std::string& message, uint16_t version) | |
| { | |
| @@ -115,6 +116,47 @@ | |
| disconnect(); | |
| } | |
| +void ProtocolLogin::getReplays(const std::string& password) | |
| +{ | |
| + uint32_t ticks = time(nullptr) / AUTHENTICATOR_PERIOD; | |
| + auto output = OutputMessagePool::getOutputMessage(); | |
| + | |
| + const std::string& motd = g_config.getString(ConfigManager::MOTD); | |
| + if (!motd.empty()) { | |
| + output->addByte(0x14); | |
| + | |
| + std::ostringstream ss; | |
| + ss << g_game.getMotdNum() << "\n" << motd; | |
| + output->addString(ss.str()); | |
| + } | |
| + | |
| + output->addByte(0x28); | |
| + output->addString(password + "\n" + std::to_string(ticks)); | |
| + | |
| + output->addByte(0x64); | |
| + output->addByte(1); | |
| + | |
| + output->addByte(0); | |
| + output->addString(g_config.getString(ConfigManager::SERVER_NAME)); | |
| + output->addString(g_config.getString(ConfigManager::IP)); | |
| + output->add<uint16_t>(g_config.getNumber(ConfigManager::REPLAY_PORT)); | |
| + output->addByte(0); | |
| + | |
| + const ReplayList& replays = g_replays.getReplays(); | |
| + uint8_t size = std::min<size_t>(std::numeric_limits<uint8_t>::max(), replays.size()); | |
| + output->addByte(size); | |
| + for (const ZReplayData& replay : replays) { | |
| + output->addByte(0); | |
| + output->addString(replay.title); | |
| + } | |
| + | |
| + output->addByte(0); | |
| + output->addByte(1); | |
| + output->add<uint32_t>(0); | |
| + | |
| + send(output); | |
| + disconnect(); | |
| +} | |
| void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) | |
| { | |
| @@ -193,16 +235,21 @@ | |
| } | |
| std::string accountName = msg.getString(); | |
| - if (accountName.empty()) { | |
| - disconnectClient("Invalid account name.", version); | |
| - return; | |
| - } | |
| + std::string password = msg.getString(); | |
| + bool replayList = false; | |
| + if (accountName == "" && password == "replaylist") { | |
| + replayList = true; | |
| + } else { | |
| + if (accountName.empty()) { | |
| + disconnectClient("Invalid account name.", version); | |
| + return; | |
| + } | |
| - std::string password = msg.getString(); | |
| - if (password.empty()) { | |
| - disconnectClient("Invalid password.", version); | |
| - return; | |
| - } | |
| + if (password.empty()) { | |
| + disconnectClient("Invalid password.", version); | |
| + return; | |
| + } | |
| + } | |
| // read authenticator token and stay logged in flag from last 128 bytes | |
| msg.skipBytes((msg.getLength() - 128) - msg.getBufferPosition()); | |
| @@ -214,5 +261,9 @@ | |
| std::string authToken = msg.getString(); | |
| auto thisPtr = std::static_pointer_cast<ProtocolLogin>(shared_from_this()); | |
| - g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountName, password, authToken, version))); | |
| -} | |
| + if (!replayList) { | |
| + g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountName, password, authToken, version))); | |
| + } else { | |
| + g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getReplays, thisPtr, password))); | |
| + } | |
| +} | |
| --- C:\Users\halls\Desktop\forgottenserver\src\protocollogin.h 2019-08-19 12:16:13.000000000 -0300 | |
| +++ C:\Users\halls\Desktop\forgotten\src\protocollogin.h 2019-08-22 18:23:46.004000000 -0300 | |
| @@ -44,6 +44,7 @@ | |
| void disconnectClient(const std::string& message, uint16_t version); | |
| void getCharacterList(const std::string& accountName, const std::string& password, const std::string& token, uint16_t version); | |
| + void getReplays(const std::string& password); | |
| }; | |
| #endif | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function onSay(player, words, param) | |
| if param == 'start' then | |
| player:sendTextMessage(MESSAGE_INFO_DESCR, 'The recording has started.') | |
| player:startReplay() | |
| elseif param == 'stop' then | |
| player:sendTextMessage(MESSAGE_INFO_DESCR, 'The recording has stoped.') | |
| player:stopReplay() | |
| else | |
| player:sendCancelMessage('use start/stop.') | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "otpch.h" | |
| #include "player.h" | |
| #include "z_replay.h" | |
| void ZReplays::saveReplay(ZReplayData& replay) | |
| { | |
| replay.recording = false; | |
| replay.title = formatDate(time(0)); | |
| ZReplayData copy = replay; | |
| // clear player replay structure to be able to record again. | |
| replay.title.clear(); | |
| PacketQueue().swap(replay.packets); | |
| replayList.push_back(std::move(copy)); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef __Z_REPLAY__ | |
| #define __Z_REPLAY__ | |
| #include "protocolgame.h" | |
| using PacketQueue = std::queue<std::pair<time_t, NetworkMessage>>; | |
| struct ZReplayData { | |
| bool recording = false; | |
| std::string title; | |
| PacketQueue packets; | |
| }; | |
| static constexpr uint8_t Z_MAX_REPLAYS = 5; | |
| using ReplayList = std::vector<ZReplayData>; | |
| class ZReplays | |
| { | |
| public: | |
| const ReplayList& getReplays() { return replayList; } | |
| void saveReplay(ZReplayData& replay); | |
| private: | |
| ReplayList replayList; | |
| }; | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment