From c35d5a2c6c64cb9ab8298a4616eacd178f2f18ae Mon Sep 17 00:00:00 2001 From: Koncord Date: Sun, 23 Apr 2017 13:39:38 +0800 Subject: [PATCH] [Master] Implement Master server --- CMakeLists.txt | 5 + apps/master/CMakeLists.txt | 26 +++++ apps/master/MasterServer.cpp | 193 +++++++++++++++++++++++++++++++++++ apps/master/MasterServer.hpp | 55 ++++++++++ apps/master/ServerTest.cpp | 186 +++++++++++++++++++++++++++++++++ apps/master/main.cpp | 30 ++++++ components/CMakeLists.txt | 2 +- 7 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 apps/master/CMakeLists.txt create mode 100644 apps/master/MasterServer.cpp create mode 100644 apps/master/MasterServer.hpp create mode 100644 apps/master/ServerTest.cpp create mode 100644 apps/master/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 169f491f4..9a4a50067 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" # Apps and tools option(BUILD_OPENMW "build OpenMW" ON) option(BUILD_OPENMW_MP "build OpenMW-MP" ON) +option(BUILD_MASTER "build tes3mp master server" OFF) option(BUILD_BSATOOL "build BSA extractor" ON) option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) @@ -571,6 +572,10 @@ if (BUILD_OPENMW_MP) add_subdirectory( apps/openmw-mp ) endif() +if (BUILD_MASTER) + add_subdirectory( apps/master ) +endif() + if (BUILD_OPENMW) add_subdirectory( apps/openmw ) endif() diff --git a/apps/master/CMakeLists.txt b/apps/master/CMakeLists.txt new file mode 100644 index 000000000..85b87048e --- /dev/null +++ b/apps/master/CMakeLists.txt @@ -0,0 +1,26 @@ +project(masterserver) + +set(CMAKE_CXX_STANDARD 14) +include_directories("./") + +set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp) + +add_executable(masterserver ${SOURCE_FILES}) +target_link_libraries(masterserver ${RakNet_LIBRARY} components) + +option(BUILD_MASTER_TEST "build master server test program" OFF) + +if(BUILD_MASTER_TEST) + add_executable(ServerTest ServerTest.cpp) + target_link_libraries(ServerTest ${RakNet_LIBRARY} components) +endif() + +if (UNIX) + # Fix for not visible pthreads functions for linker with glibc 2.15 + if(NOT APPLE) + target_link_libraries(masterserver ${CMAKE_THREAD_LIBS_INIT}) + if(BUILD_MASTER_TEST) + target_link_libraries(ServerTest ${CMAKE_THREAD_LIBS_INIT}) + endif() + endif(NOT APPLE) +endif(UNIX) diff --git a/apps/master/MasterServer.cpp b/apps/master/MasterServer.cpp new file mode 100644 index 000000000..e0622b391 --- /dev/null +++ b/apps/master/MasterServer.cpp @@ -0,0 +1,193 @@ +// +// Created by koncord on 21.04.17. +// + +#include +#include +#include +#include +#include "MasterServer.hpp" + +#include +#include +#include + +using namespace RakNet; +using namespace std; +using namespace mwmp; + +MasterServer::MasterServer(unsigned short maxConnections, unsigned short port) +{ + peer = RakPeerInterface::GetInstance(); + sockdescr = SocketDescriptor(port, 0); + peer->Startup(maxConnections, &sockdescr, 1, 1000); + + peer->SetMaximumIncomingConnections(maxConnections); + peer->SetIncomingPassword("pass", (int) strlen("pass")); + run = false; +} + +MasterServer::~MasterServer() +{ + Stop(true); +} + +using namespace chrono; + +void MasterServer::Thread() +{ + unsigned char packetId = 0; + + auto startTime = chrono::steady_clock::now(); + while (run) + { + Packet *packet = peer->Receive(); + + auto now = chrono::steady_clock::now(); + if (now - startTime >= 60s) + { + startTime = chrono::steady_clock::now(); + for (auto it = servers.begin(); it != servers.end();) + { + + if (it->second.lastUpdate + 60s <= now) + servers.erase(it++); + else ++it; + } + } + + if (packet == nullptr) + RakSleep(10); + else + for (; packet; peer->DeallocatePacket(packet), packet = peer->Receive()) + { + BitStream data(packet->data, packet->length, false); + data.Read(packetId); + switch (packetId) + { + case ID_NEW_INCOMING_CONNECTION: + cout << "New incoming connection: " << packet->systemAddress.ToString() << endl; + break; + case ID_DISCONNECTION_NOTIFICATION: + cout << "Disconnected: " << packet->systemAddress.ToString() << endl; + break; + case ID_CONNECTION_LOST: + cout << "Connection lost: " << packet->systemAddress.ToString() << endl; + break; + case ID_MASTER_QUERY: + { + BitStream send; + PacketMasterQuery pmq(peer); + pmq.SetSendStream(&send); + pmq.SetServers(reinterpret_cast *>(&servers)); + pmq.Send(packet->systemAddress); + + cout << "Sent info about all " << servers.size() << " servers to " + << packet->systemAddress.ToString() << endl; + peer->CloseConnection(packet->systemAddress, true); + break; + } + case ID_MASTER_UPDATE: + { + SystemAddress addr; + data.Read(addr); // update 1 server + + ServerIter it = servers.find(addr); + if (it != servers.end()) + { + mwmp::PacketMasterUpdate pmu(peer); + BitStream send; + pmu.SetSendStream(&send); + pair pairPtr(it->first, static_cast(it->second)); + pmu.SetServer(&pairPtr); + pmu.Send(packet->systemAddress); + cout << "Sent info about " << addr.ToString() << " to " << packet->systemAddress.ToString() + << endl; + } + peer->CloseConnection(packet->systemAddress, true); + break; + } + case ID_MASTER_ANNOUNCE: + { + ServerIter iter = servers.find(packet->systemAddress); + + PacketMasterAnnounce pma(peer); + pma.SetReadStream(&data); + + SServer server; + pma.SetServer(&server); + pma.Read(); + + switch (pma.GetFunc()) + { + case PacketMasterAnnounce::FUNCTION_DELETE: + { + if (iter != servers.end()) + servers.erase(iter); + cout << "Deleted"; + break; + } + case PacketMasterAnnounce::FUNCTION_ANNOUNCE: + { + + if (iter == servers.end()) + cout << "Added"; + else + cout << "Updated"; + iter = servers.insert({packet->systemAddress, server}).first; + break; + } + default: + cout << "Keeping alive"; + } + cout << " server " << packet->systemAddress.ToString() << endl; + + if (pma.GetFunc() != PacketMasterAnnounce::FUNCTION_DELETE) + iter->second.lastUpdate = now; + peer->CloseConnection(packet->systemAddress, true); + break; + } + default: + cout << "Wrong packet" << endl; + peer->CloseConnection(packet->systemAddress, true); + } + } + } + peer->Shutdown(1000); + RakPeerInterface::DestroyInstance(peer); + cout << "Server thread stopped" << endl; +} + +void MasterServer::Start() +{ + if (!run) + { + run = true; + tMasterThread = thread(&MasterServer::Thread, this); + cout << "Started" << endl; + } +} + +void MasterServer::Stop(bool wait) +{ + if (run) + { + run = false; + if (wait && tMasterThread.joinable()) + tMasterThread.join(); + } +} + +bool MasterServer::isRunning() +{ + return run; +} + +void MasterServer::Wait() +{ + if (run) + { + if (tMasterThread.joinable()) + tMasterThread.join(); + } +} diff --git a/apps/master/MasterServer.hpp b/apps/master/MasterServer.hpp new file mode 100644 index 000000000..556c23628 --- /dev/null +++ b/apps/master/MasterServer.hpp @@ -0,0 +1,55 @@ +// +// Created by koncord on 21.04.17. +// + +#ifndef NEWMASTERPROTO_MASTERSERVER_HPP +#define NEWMASTERPROTO_MASTERSERVER_HPP + +#include +#include +#include +#include + +class MasterServer +{ +public: + MasterServer(unsigned short maxConnections, unsigned short port); + ~MasterServer(); + + void Start(); + void Stop(bool wait = false); + bool isRunning(); + void Wait(); + + struct SServer : Server + { + std::chrono::steady_clock::time_point lastUpdate; + }; + + struct Ban + { + RakNet::SystemAddress sa; + bool permanent; + struct Date + { + + } date; + }; + + typedef std::map ServerMap; + typedef ServerMap::const_iterator ServerCIter; + typedef ServerMap::iterator ServerIter; + +private: + void Thread(); + +private: + std::thread tMasterThread; + RakNet::RakPeerInterface* peer; + RakNet::SocketDescriptor sockdescr; + ServerMap servers; + bool run; +}; + + +#endif //NEWMASTERPROTO_MASTERSERVER_HPP diff --git a/apps/master/ServerTest.cpp b/apps/master/ServerTest.cpp new file mode 100644 index 000000000..56f4ee835 --- /dev/null +++ b/apps/master/ServerTest.cpp @@ -0,0 +1,186 @@ +// +// Created by koncord on 21.04.17. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace RakNet; +using namespace mwmp; + +int main() +{ + cout << "Server test" << endl; + + SystemAddress masterAddr("127.0.0.1", 25560); + + RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance(); + + RakNet::SocketDescriptor sd(25565, 0); + peer->Startup(8, &sd, 1); + + ConnectionAttemptResult result = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), "pass", + (int)(strlen("pass")), 0, 0, 5, 500); + + assert(result == RakNet::CONNECTION_ATTEMPT_STARTED); + + char message[2048]; + BitStream send; + + PacketMasterQuery pmq(peer); + pmq.SetSendStream(&send); + + PacketMasterAnnounce pma(peer); + pma.SetSendStream(&send); + + while (true) + { + RakSleep(30); + + if (kbhit()) + { + Gets(message, sizeof(message)); + + if (strcmp(message, "quit") == 0) + { + puts("Quitting."); + break; + } + else if (strcmp(message, "send") == 0) + { + puts("Sending data about server"); + Server server; + server.SetName("Super Server"); + server.SetPlayers(0); + server.SetMaxPlayers(0); + + pma.SetServer(&server); + pma.SetFunc(PacketMasterAnnounce::FUNCTION_ANNOUNCE); + pma.Send(masterAddr); + } + else if (strcmp(message, "get") == 0) + { + puts("Request query info"); + send.Reset(); + send.Write((unsigned char) (ID_MASTER_QUERY)); + peer->Send(&send, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false); + } + else if (strcmp(message, "getme") == 0) + { + send.Reset(); + send.Write((unsigned char) (ID_MASTER_UPDATE)); + send.Write(SystemAddress("127.0.0.1", 25565)); + peer->Send(&send, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false); + } + else if (strcmp(message, "status") == 0) + { + cout << (peer->GetConnectionState(masterAddr) == IS_CONNECTED ? "Connected" : "Not connected") << endl; + } + else if (strcmp(message, "keep") == 0) + { + cout << "Sending keep alive" << endl; + pma.SetFunc(PacketMasterAnnounce::FUNCTION_KEEP); + pma.Send(masterAddr); + } + } + + for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive()) + { + BitStream data(packet->data, packet->length, false); + unsigned char packetID; + data.Read(packetID); + switch (packetID) + { + case ID_DISCONNECTION_NOTIFICATION: + // Connection lost normally + printf("ID_DISCONNECTION_NOTIFICATION\n"); + break; + case ID_ALREADY_CONNECTED: + // Connection lost normally + printf("ID_ALREADY_CONNECTED with guid %lu\n", packet->guid.g); + break; + case ID_INCOMPATIBLE_PROTOCOL_VERSION: + printf("ID_INCOMPATIBLE_PROTOCOL_VERSION\n"); + break; + case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want. + printf("ID_REMOTE_DISCONNECTION_NOTIFICATION\n"); + break; + case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want. + printf("ID_REMOTE_CONNECTION_LOST\n"); + break; + case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want. + printf("ID_REMOTE_NEW_INCOMING_CONNECTION\n"); + break; + case ID_CONNECTION_BANNED: // Banned from this server + printf("We are banned from this server.\n"); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + printf("Connection attempt failed\n"); + break; + case ID_NO_FREE_INCOMING_CONNECTIONS: + // Sorry, the server is full. I don't do anything here but + // A real app should tell the user + printf("ID_NO_FREE_INCOMING_CONNECTIONS\n"); + break; + + case ID_INVALID_PASSWORD: + printf("ID_INVALID_PASSWORD\n"); + break; + + case ID_CONNECTION_LOST: + // Couldn't deliver a reliable packet - i.e. the other system was abnormally + // terminated + printf("ID_CONNECTION_LOST\n"); + return 0; + break; + + case ID_CONNECTION_REQUEST_ACCEPTED: + // This tells the client they have connected + printf("ID_CONNECTION_REQUEST_ACCEPTED to %s with GUID %s\n", packet->systemAddress.ToString(true), + packet->guid.ToString()); + printf("My external address is %s\n", peer->GetExternalID(packet->systemAddress).ToString(true)); + break; + case ID_MASTER_QUERY: + { + map servers; + + pmq.SetReadStream(&data); + pmq.SetServers(&servers); + pmq.Read(); + + cout << "Received query data about " << servers.size() << " servers" << endl; + + for (auto serv : servers) + cout << serv.second.GetName() << endl; + + break; + } + case ID_MASTER_UPDATE: + { + pair serverPair; + PacketMasterUpdate pmu(peer); + pmu.SetReadStream(&data); + pmu.SetServer(&serverPair); + pmu.Read(); + cout << "Received info about " << serverPair.first.ToString() << endl; + cout << serverPair.second.GetName() << endl; + break; + } + default: + cout << "Wrong packet" << endl; + } + } + } + peer->Shutdown(1000); + RakPeerInterface::DestroyInstance(peer); +} \ No newline at end of file diff --git a/apps/master/main.cpp b/apps/master/main.cpp new file mode 100644 index 000000000..80855837a --- /dev/null +++ b/apps/master/main.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include "MasterServer.hpp" + +using namespace RakNet; +using namespace std; + +int main() +{ + MasterServer masterServer(2000, 25560); + + masterServer.Start(); + + /*while(true) + { + if(kbhit()) + { + if(getch() == 'e') + { + cout << endl; + masterServer.Stop(true); + break; + } + } + RakSleep(100); + }*/ + masterServer.Wait(); + return 0; +} \ No newline at end of file diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index fef7980a3..e48435fd2 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -160,7 +160,7 @@ add_component_dir (openmw-mp\\Controllers ) add_component_dir(openmw-mp\\Master - MasterData PacketMasterQuery PacketMasterUpdate PacketMasterAnnounce BaseMasterPacket + MasterData PacketMasterQuery PacketMasterUpdate PacketMasterAnnounce BaseMasterPacket ProxyMasterPacket ) add_component_dir (openmw-mp\\Packets