Last active
August 27, 2023 21:40
-
-
Save rookgaard/64834fc1de5414ea625cd21b3e9f427a to your computer and use it in GitHub Desktop.
cams by gesior
This file contains 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
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt | |
index 5540fa58..48439138 100644 | |
--- a/src/CMakeLists.txt | |
+++ b/src/CMakeLists.txt | |
@@ -4,6 +4,7 @@ set(tfs_SRC | |
${CMAKE_CURRENT_LIST_DIR}/ban.cpp | |
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp | |
${CMAKE_CURRENT_LIST_DIR}/bed.cpp | |
+ ${CMAKE_CURRENT_LIST_DIR}/cams.cpp | |
${CMAKE_CURRENT_LIST_DIR}/chat.cpp | |
${CMAKE_CURRENT_LIST_DIR}/combat.cpp | |
${CMAKE_CURRENT_LIST_DIR}/condition.cpp | |
diff --git a/src/cams.cpp b/src/cams.cpp | |
new file mode 100644 | |
index 00000000..1916b5a0 | |
--- /dev/null | |
+++ b/src/cams.cpp | |
@@ -0,0 +1,216 @@ | |
+#include "otpch.h" | |
+ | |
+#include "cams.h" | |
+#include "configmanager.h" | |
+ | |
+extern ConfigManager g_config; | |
+ | |
+void Cams::threadMain() | |
+{ | |
+ if (!g_config.getBoolean(ConfigManager::CAMS_ENABLED)) { | |
+ camSystemEnabled = false; | |
+ std::cout << "Cams System disabled" << std::endl; | |
+ return; | |
+ } | |
+ | |
+ boost::filesystem::path camsDirectory(g_config.getString(ConfigManager::CAMS_DIRECTORY)); | |
+ | |
+ if (!boost::filesystem::exists(camsDirectory) || !boost::filesystem::is_directory(camsDirectory)) { | |
+ camSystemEnabled = false; | |
+ std::cout << "[Warning - Cams::threadMain] Configured Cams directory does not exist or is not a directory: '"; | |
+ std::cout << camsDirectory << "'\n" << "[Warning - Cams::threadMain] Cam System disabled" << std::endl; | |
+ return; | |
+ } | |
+ | |
+ while (true) { | |
+ processAllCams(camsDirectory, false); | |
+ std::this_thread::sleep_for(std::chrono::milliseconds(250)); | |
+ | |
+ if (getState() == THREAD_STATE_TERMINATED) { | |
+ processAllCams(camsDirectory, true); | |
+ break; | |
+ } | |
+ } | |
+} | |
+ | |
+void Cams::processAllCams(const boost::filesystem::path &camsDirectory, bool serverShutdown) | |
+{ | |
+ auto *playerCamsToWriteToDisk = new std::vector<PlayerCam>(); | |
+ auto *playerCamsToClose = new std::vector<PlayerCam>(); | |
+ generateCamsToProcessLists(playerCamsToWriteToDisk, playerCamsToClose, serverShutdown); | |
+ | |
+ writeCamsToDisk(playerCamsToWriteToDisk, camsDirectory); | |
+ playerCamsToWriteToDisk->clear(); | |
+ | |
+ closeCams(playerCamsToClose, camsDirectory); | |
+ playerCamsToClose->clear(); | |
+ delete playerCamsToWriteToDisk; | |
+ delete playerCamsToClose; | |
+} | |
+ | |
+void Cams::generateCamsToProcessLists(std::vector<PlayerCam> *playerCamsToWriteToDisk, | |
+ std::vector<PlayerCam> *playerCamsToClose, | |
+ bool closeAllCams) | |
+{ | |
+ int memoryBufferPacketsNumber = g_config.getNumber(ConfigManager::CAMS_MEMORY_BUFFER_PACKETS_NUMBER); | |
+ int closeCamIfNoPacketsForSeconds = g_config.getNumber(ConfigManager::CAMS_CLOSE_CAM_IF_NO_PACKETS_FOR_SECONDS); | |
+ | |
+ std::lock_guard<std::mutex> lockClass(playerCamsLock); | |
+ | |
+ for (auto it = playerCams.begin(); it != playerCams.end();) { | |
+ auto playerCam = it->second; | |
+ if (playerCam.lastPacket + closeCamIfNoPacketsForSeconds < time(nullptr) || closeAllCams) { | |
+ playerCamsToWriteToDisk->push_back(playerCam); | |
+ playerCamsToClose->push_back(playerCam); | |
+ it = playerCams.erase(it); | |
+ } else if (playerCam.packets->size() > memoryBufferPacketsNumber) { | |
+ playerCamsToWriteToDisk->push_back(playerCam); | |
+ playerCam.packets = std::make_shared<PacketsQueue>(); | |
+ ++it; | |
+ } else { | |
+ ++it; | |
+ } | |
+ } | |
+} | |
+ | |
+void Cams::writeCamsToDisk(std::vector<PlayerCam> *playerCamsToWriteToDisk, | |
+ const boost::filesystem::path &camsDirectory) | |
+{ | |
+ for (auto playerCam: *playerCamsToWriteToDisk) { | |
+ std::string camTmpFilePath = getCamTmpFilePath(camsDirectory, playerCam); | |
+ | |
+ std::ofstream camFileOutput(camTmpFilePath, std::ofstream::out | std::ofstream::app); | |
+ if (!camFileOutput.is_open()) { | |
+ std::cout << "[Warning - Cams::threadMain] Cannot open '" << camTmpFilePath << "'" << std::endl; | |
+ continue; | |
+ } | |
+ | |
+ auto packets = playerCam.packets; | |
+ while (!packets->empty()) { | |
+ auto packet = packets->front(); | |
+ packets->pop(); | |
+ | |
+ if (packet.type == TYPE_INPUT) { | |
+ camFileOutput << ">"; | |
+ } else { | |
+ camFileOutput << "<"; | |
+ } | |
+ camFileOutput << " " << (packet.time - playerCam.startTime) << " "; | |
+ camFileOutput << std::hex; | |
+ for (uint8_t &byte: packet.bytes) { | |
+ camFileOutput << (int) (byte / 16) << (int) (byte % 16); | |
+ } | |
+ camFileOutput << std::endl; | |
+ camFileOutput << std::dec; | |
+ } | |
+ camFileOutput.close(); | |
+ } | |
+} | |
+ | |
+void Cams::closeCams(std::vector<PlayerCam> *playerCamsToClose, const boost::filesystem::path &camsDirectory) | |
+{ | |
+ for (auto playerCam: *playerCamsToClose) { | |
+ std::string camTmpFilePath = getCamTmpFilePath(camsDirectory, playerCam); | |
+ std::string camFilePath = getCamFilePath(camsDirectory, playerCam); | |
+ if (std::rename(camTmpFilePath.c_str(), camFilePath.c_str())) { | |
+ std::cout << "[Warning - Cams::threadMain] Failed to move temporary Cam file from '" | |
+ << camTmpFilePath << "' to '" << camFilePath << "'" << std::endl; | |
+ } | |
+ } | |
+} | |
+ | |
+std::string Cams::getCamFilePath(const boost::filesystem::path &camsDirectory, PlayerCam &playerCam) | |
+{ | |
+ boost::filesystem::path camPath = camsDirectory; | |
+ camPath /= std::to_string(playerCam.playerId) + "." + std::to_string(playerCam.startTime) + ".cam"; | |
+ return camPath.string(); | |
+} | |
+ | |
+std::string Cams::getCamTmpFilePath(const boost::filesystem::path &camsDirectory, PlayerCam &playerCam) | |
+{ | |
+ boost::filesystem::path camPath = camsDirectory; | |
+ camPath /= std::to_string(playerCam.playerId) + "." + std::to_string(playerCam.startTime) + ".cam.tmp"; | |
+ return camPath.string(); | |
+} | |
+ | |
+uint64_t Cams::startCam(uint32_t playerId, uint32_t playerLevel, uint32_t accountId, uint32_t ip) | |
+{ | |
+ if (!camSystemEnabled) { | |
+ return 0; | |
+ } | |
+ | |
+ std::lock_guard<std::mutex> lockClass(playerCamsLock); | |
+ | |
+ ++currentCamId; | |
+ playerCams[currentCamId] = PlayerCam( | |
+ { | |
+ currentCamId, | |
+ std::make_shared<PacketsQueue>(), | |
+ OTSYS_TIME(), | |
+ time(nullptr), | |
+ playerId, | |
+ playerLevel, | |
+ accountId, | |
+ ip | |
+ } | |
+ ); | |
+ | |
+ return currentCamId; | |
+} | |
+ | |
+void Cams::addInputPacket(uint64_t camId, const NetworkMessage &msg) | |
+{ | |
+ if (!camSystemEnabled) { | |
+ return; | |
+ } | |
+ | |
+ if (!g_config.getBoolean(ConfigManager::CAMS_RECORD_INPUT_PACKETS)) { | |
+ return; | |
+ } | |
+ | |
+ std::lock_guard<std::mutex> lockClass(playerCamsLock); | |
+ | |
+ auto it = playerCams.find(camId); | |
+ if (it == playerCams.end()) { | |
+ return; | |
+ } | |
+ | |
+ int startBufferPosition = msg.getBufferPosition() - 1; | |
+ int lastBufferPosition = msg.getBufferPosition() + msg.getLength() - 1; | |
+ std::vector<uint8_t> buffer(msg.getBuffer() + startBufferPosition, msg.getBuffer() + lastBufferPosition); | |
+ | |
+ it->second.lastPacket = time(nullptr); | |
+ it->second.packets->push(Packet({OTSYS_TIME(), TYPE_INPUT, std::move(buffer)})); | |
+} | |
+ | |
+void Cams::addOutputPacket(uint64_t camId, const NetworkMessage &msg) | |
+{ | |
+ if (!camSystemEnabled) { | |
+ return; | |
+ } | |
+ | |
+ if (!g_config.getBoolean(ConfigManager::CAMS_RECORD_OUTPUT_PACKETS)) { | |
+ return; | |
+ } | |
+ | |
+ std::lock_guard<std::mutex> lockClass(playerCamsLock); | |
+ | |
+ auto it = playerCams.find(camId); | |
+ if (it == playerCams.end()) { | |
+ return; | |
+ } | |
+ | |
+ int startBufferPosition = NetworkMessage::INITIAL_BUFFER_POSITION; | |
+ int lastBufferPosition = msg.getBufferPosition(); | |
+ std::vector<uint8_t> buffer(msg.getBuffer() + startBufferPosition, msg.getBuffer() + lastBufferPosition); | |
+ | |
+ it->second.lastPacket = time(nullptr); | |
+ it->second.packets->push(Packet({OTSYS_TIME(), TYPE_OUTPUT, std::move(buffer)})); | |
+} | |
+ | |
+void Cams::shutdown() | |
+{ | |
+ std::lock_guard<std::mutex> lockClass(playerCamsLock); | |
+ | |
+ setState(THREAD_STATE_TERMINATED); | |
+} | |
diff --git a/src/cams.h b/src/cams.h | |
new file mode 100644 | |
index 00000000..0baaf64f | |
--- /dev/null | |
+++ b/src/cams.h | |
@@ -0,0 +1,65 @@ | |
+#ifndef FS_CAMS_H | |
+#define FS_CAMS_H | |
+ | |
+#include <condition_variable> | |
+#include <map> | |
+#include <vector> | |
+#include <queue> | |
+#include <boost/filesystem.hpp> | |
+#include "thread_holder_base.h" | |
+#include "enums.h" | |
+#include "networkmessage.h" | |
+#include "tools.h" | |
+ | |
+struct Packet; | |
+using PacketsQueue = std::queue<Packet>; | |
+using PacketsQueue_ptr = std::shared_ptr<PacketsQueue>; | |
+ | |
+enum CamPacketType { | |
+ TYPE_INPUT, | |
+ TYPE_OUTPUT | |
+}; | |
+ | |
+struct PlayerCam { | |
+ uint64_t camId; | |
+ PacketsQueue_ptr packets; | |
+ int64_t startTime; | |
+ time_t lastPacket; | |
+ uint32_t playerId; | |
+ uint32_t playerLevel; | |
+ uint32_t accountId; | |
+ uint32_t ip; | |
+}; | |
+ | |
+struct Packet { | |
+ int64_t time; | |
+ CamPacketType type; | |
+ std::vector<uint8_t> bytes; | |
+}; | |
+ | |
+class Cams : public ThreadHolder<Cams> { | |
+public: | |
+ uint64_t startCam(uint32_t playerId, uint32_t playerLevel, uint32_t accountId, uint32_t ip); | |
+ void addInputPacket(uint64_t camId,const NetworkMessage& msg); | |
+ void addOutputPacket(uint64_t camId, const NetworkMessage& msg); | |
+ | |
+ void shutdown(); | |
+ void threadMain(); | |
+ | |
+private: | |
+ void processAllCams(const boost::filesystem::path& camsDirectory, bool serverShutdown); | |
+ void generateCamsToProcessLists(std::vector<PlayerCam>* playerCamsToWriteToDisk, std::vector<PlayerCam>* playerCamsToClose, bool closeAllCams); | |
+ static void writeCamsToDisk(std::vector<PlayerCam>* playerCamsToWriteToDisk, const boost::filesystem::path& camsDirectory); | |
+ static void closeCams(std::vector<PlayerCam>* playerCamsToClose, const boost::filesystem::path& camsDirectory); | |
+ static std::string getCamFilePath(const boost::filesystem::path& camsDirectory, PlayerCam &playerCam); | |
+ static std::string getCamTmpFilePath(const boost::filesystem::path& camsDirectory, PlayerCam &playerCam); | |
+ | |
+ std::mutex playerCamsLock; | |
+ bool camSystemEnabled = true; | |
+ uint64_t currentCamId = 1; | |
+ std::map<uint64_t, PlayerCam> playerCams; | |
+}; | |
+ | |
+extern Cams g_cams; | |
+ | |
+#endif | |
diff --git a/src/configmanager.cpp b/src/configmanager.cpp | |
index 59bae60c..58ba9093 100644 | |
--- a/src/configmanager.cpp | |
+++ b/src/configmanager.cpp | |
@@ -264,6 +264,9 @@ bool ConfigManager::load() | |
boolean[ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS] = getGlobalBoolean(L, "onlyInvitedCanMoveHouseItems", true); | |
boolean[REMOVE_ON_DESPAWN] = getGlobalBoolean(L, "removeOnDespawn", true); | |
boolean[PLAYER_CONSOLE_LOGS] = getGlobalBoolean(L, "showPlayerLogInConsole", true); | |
+ boolean[CAMS_ENABLED] = getGlobalBoolean(L, "camsEnabled", false); | |
+ boolean[CAMS_RECORD_INPUT_PACKETS] = getGlobalBoolean(L, "camsRecordInputPackets", false); | |
+ boolean[CAMS_RECORD_OUTPUT_PACKETS] = getGlobalBoolean(L, "camsRecordOutputPackets", false); | |
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high"); | |
string[SERVER_NAME] = getGlobalString(L, "serverName", ""); | |
@@ -273,6 +276,7 @@ bool ConfigManager::load() | |
string[LOCATION] = getGlobalString(L, "location", ""); | |
string[MOTD] = getGlobalString(L, "motd", ""); | |
string[WORLD_TYPE] = getGlobalString(L, "worldType", "pvp"); | |
+ string[CAMS_DIRECTORY] = getGlobalString(L, "camsDirectory", "cams/"); | |
integer[MAX_PLAYERS] = getGlobalNumber(L, "maxPlayers"); | |
integer[PZ_LOCKED] = getGlobalNumber(L, "pzLocked", 60000); | |
@@ -308,6 +312,8 @@ bool ConfigManager::load() | |
integer[VIP_PREMIUM_LIMIT] = getGlobalNumber(L, "vipPremiumLimit", 100); | |
integer[DEPOT_FREE_LIMIT] = getGlobalNumber(L, "depotFreeLimit", 2000); | |
integer[DEPOT_PREMIUM_LIMIT] = getGlobalNumber(L, "depotPremiumLimit", 10000); | |
+ integer[CAMS_MEMORY_BUFFER_PACKETS_NUMBER] = getGlobalNumber(L, "camsMemoryBufferPacketsNumber", 5000); | |
+ integer[CAMS_CLOSE_CAM_IF_NO_PACKETS_FOR_SECONDS] = getGlobalNumber(L, "camsCloseCamIfNoPacketsForSeconds", 20); | |
expStages = loadXMLStages(); | |
if (expStages.empty()) { | |
diff --git a/src/configmanager.h b/src/configmanager.h | |
index 3871d029..6f8b5882 100644 | |
--- a/src/configmanager.h | |
+++ b/src/configmanager.h | |
@@ -69,6 +69,9 @@ class ConfigManager | |
ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS, | |
REMOVE_ON_DESPAWN, | |
PLAYER_CONSOLE_LOGS, | |
+ CAMS_ENABLED, | |
+ CAMS_RECORD_INPUT_PACKETS, | |
+ CAMS_RECORD_OUTPUT_PACKETS, | |
LAST_BOOLEAN_CONFIG /* this must be the last one */ | |
}; | |
@@ -92,6 +95,7 @@ class ConfigManager | |
DEFAULT_PRIORITY, | |
MAP_AUTHOR, | |
CONFIG_FILE, | |
+ CAMS_DIRECTORY, | |
LAST_STRING_CONFIG /* this must be the last one */ | |
}; | |
@@ -137,6 +141,8 @@ class ConfigManager | |
DEPOT_FREE_LIMIT, | |
DEPOT_PREMIUM_LIMIT, | |
IP_NUM, | |
+ CAMS_MEMORY_BUFFER_PACKETS_NUMBER, | |
+ CAMS_CLOSE_CAM_IF_NO_PACKETS_FOR_SECONDS, | |
LAST_INTEGER_CONFIG /* this must be the last one */ | |
}; | |
diff --git a/src/game.cpp b/src/game.cpp | |
index 5066c63c..4a7136b2 100644 | |
--- a/src/game.cpp | |
+++ b/src/game.cpp | |
@@ -41,6 +41,7 @@ | |
#include "talkaction.h" | |
#include "weapons.h" | |
#include "script.h" | |
+#include "cams.h" | |
#include <fmt/format.h> | |
@@ -152,6 +153,7 @@ void Game::setGameState(GameState_t newState) | |
g_scheduler.stop(); | |
g_databaseTasks.stop(); | |
g_dispatcher.stop(); | |
+ g_cams.stop(); | |
break; | |
} | |
@@ -4687,6 +4689,7 @@ void Game::shutdown() | |
g_scheduler.shutdown(); | |
g_databaseTasks.shutdown(); | |
g_dispatcher.shutdown(); | |
+ g_cams.shutdown(); | |
map.spawns.clear(); | |
raids.clear(); | |
diff --git a/src/otserv.cpp b/src/otserv.cpp | |
index a7773e3a..7c11bd74 100644 | |
--- a/src/otserv.cpp | |
+++ b/src/otserv.cpp | |
@@ -25,6 +25,7 @@ | |
#include "iomarket.h" | |
+#include "cams.h" | |
#include "configmanager.h" | |
#include "scriptmanager.h" | |
#include "rsa.h" | |
@@ -50,6 +51,7 @@ ConfigManager g_config; | |
Monsters g_monsters; | |
Vocations g_vocations; | |
extern Scripts* g_scripts; | |
+Cams g_cams; | |
RSA g_RSA; | |
std::mutex g_loaderLock; | |
@@ -100,11 +102,13 @@ int main(int argc, char* argv[]) | |
g_scheduler.shutdown(); | |
g_databaseTasks.shutdown(); | |
g_dispatcher.shutdown(); | |
+ g_cams.shutdown(); | |
} | |
g_scheduler.join(); | |
g_databaseTasks.join(); | |
g_dispatcher.join(); | |
+ g_cams.join(); | |
return 0; | |
} | |
@@ -333,6 +337,8 @@ void mainLoader(int, char*[], ServiceManager* services) | |
} | |
#endif | |
+ g_cams.start(); | |
+ | |
g_game.start(services); | |
g_game.setGameState(GAME_STATE_NORMAL); | |
g_loaderSignal.notify_all(); | |
diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp | |
index 274b590d..e6e8b6d9 100644 | |
--- a/src/protocolgame.cpp | |
+++ b/src/protocolgame.cpp | |
@@ -32,6 +32,7 @@ | |
#include "iomarket.h" | |
#include "ban.h" | |
#include "scheduler.h" | |
+#include "cams.h" | |
#include <fmt/format.h> | |
@@ -221,6 +222,8 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS | |
player->setOperatingSystem(operatingSystem); | |
+ camId = g_cams.startCam(player->getGUID(), player->getLevel(), player->getAccount(), getIP()); | |
+ | |
if (!g_game.placeCreature(player, player->getLoginPosition())) { | |
if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) { | |
disconnectClient("Temple position is wrong. Contact the administrator."); | |
@@ -279,6 +282,9 @@ void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem) | |
player->isConnecting = false; | |
player->client = getThis(); | |
+ | |
+ camId = g_cams.startCam(player->getGUID(), player->getLevel(), player->getAccount(), getIP()); | |
+ | |
sendAddCreature(player, player->getPosition(), 0, false); | |
player->lastIP = player->getIP(); | |
player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1); | |
@@ -479,6 +485,10 @@ void ProtocolGame::disconnectClient(const std::string& message) const | |
void ProtocolGame::writeToOutputBuffer(const NetworkMessage& msg) | |
{ | |
+ if (camId) { | |
+ g_cams.addOutputPacket(camId, msg); | |
+ } | |
+ | |
auto out = getOutputBuffer(msg.getLength()); | |
out->append(msg); | |
} | |
@@ -511,6 +521,10 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) | |
} | |
} | |
+ if (camId) { | |
+ g_cams.addInputPacket(camId, msg); | |
+ } | |
+ | |
switch (recvbyte) { | |
case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break; | |
case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break; | |
diff --git a/src/protocolgame.h b/src/protocolgame.h | |
index c9895d6c..c1f74181 100644 | |
--- a/src/protocolgame.h | |
+++ b/src/protocolgame.h | |
@@ -339,6 +339,7 @@ class ProtocolGame final : public Protocol | |
bool debugAssertSent = false; | |
bool acceptPackets = false; | |
+ uint64_t camId = 0; | |
}; | |
#endif | |
diff --git a/src/signals.cpp b/src/signals.cpp | |
index adc6a61c..2c107013 100644 | |
--- a/src/signals.cpp | |
+++ b/src/signals.cpp | |
@@ -37,10 +37,12 @@ | |
#include "events.h" | |
#include "scheduler.h" | |
#include "databasetasks.h" | |
+#include "cams.h" | |
extern Scheduler g_scheduler; | |
extern DatabaseTasks g_databaseTasks; | |
extern Dispatcher g_dispatcher; | |
+extern Cams g_cams; | |
extern ConfigManager g_config; | |
extern Actions* g_actions; | |
@@ -173,6 +175,7 @@ void dispatchSignalHandler(int signal) | |
g_scheduler.join(); | |
g_databaseTasks.join(); | |
g_dispatcher.join(); | |
+ g_cams.join(); | |
break; | |
#endif | |
default: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment