diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 184418e5..33307df9 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -12,6 +12,7 @@ namespace Components Loader::Register(new Dvar()); Loader::Register(new Maps()); Loader::Register(new News()); + Loader::Register(new Node()); Loader::Register(new Menus()); Loader::Register(new Party()); Loader::Register(new Colors()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 300a8746..0d492892 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -35,6 +35,7 @@ namespace Components #include "Modules\IPCPipe.hpp" #include "Modules\Network.hpp" #include "Modules\Theatre.hpp" +#include "Modules\Node.hpp" #include "Modules\Party.hpp" // Destroys the order, but requires network classes :D #include "Modules\Download.hpp" #include "Modules\Playlist.hpp" diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 51457d0a..fd32f3f8 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -7,7 +7,7 @@ namespace Components bool Logger::IsConsoleReady() { - return (IsWindow(*(HWND*)0x64A3288) != FALSE); + return (IsWindow(*(HWND*)0x64A3288) != FALSE || (Dedicated::IsDedicated() && !Flags::HasFlag("console"))); } void Logger::Print(const char* message, ...) diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index c0627daa..ac2dfa7f 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -79,6 +79,8 @@ namespace Components } } + // TODO: Check the external IP as well! + return false; } diff --git a/src/Components/Modules/Node.cpp b/src/Components/Modules/Node.cpp new file mode 100644 index 00000000..b49b280a --- /dev/null +++ b/src/Components/Modules/Node.cpp @@ -0,0 +1,300 @@ +#include "STDInclude.hpp" + +namespace Components +{ + std::vector Node::Nodes; + std::vector Node::Dedis; + + void Node::LoadNodes() + { + std::string nodes = Utils::ReadFile("nodes.txt"); + auto list = Utils::Explode(nodes, '\n'); + + for (auto entry : list) + { + Network::Address addr(entry); + Node::AddNode(addr); + } + } + + void Node::LoadDedis() + { + + } + + void Node::StoreNodes() + { + std::string nodes; + + for (auto node : Node::Nodes) + { + nodes.append(node.address.GetString()); + nodes.append("\n"); + } + + Utils::WriteFile("nodes.txt", nodes); + } + + void Node::StoreDedis() + { + + } + + void Node::AddNode(Network::Address address, bool valid) + { + //if (address.IsLocal() || address.IsSelf()) return; + + Node::NodeEntry entry; + + entry.startTime = 0; + entry.endTime = (valid ? Game::Com_Milliseconds() : 0); + entry.state = (valid ? Node::STATE_VALID : Node::STATE_UNKNOWN); + entry.address = address; + + // Search if we already know that node + bool duplicate = false; + for (auto &ourEntry : Node::Nodes) + { + if (ourEntry.address == entry.address) + { + // Validate it + if (valid) + { + ourEntry.state = Node::STATE_VALID; + ourEntry.endTime = Game::Com_Milliseconds(); + } + + duplicate = true; + break; + } + } + + // Insert if we don't + if (!duplicate) + { + Node::Nodes.push_back(entry); + } + } + + void Node::AddDedi(Network::Address address) + { + Node::DediEntry entry; + + entry.startTime = 0; + entry.endTime = 0; + entry.state = Node::STATE_UNKNOWN; + entry.address = address; + + // Search if we already know that node + bool duplicate = false; + for (auto ourEntry : Node::Nodes) + { + if (ourEntry.address == entry.address) + { + duplicate = true; + break; + } + } + + // Insert if we don't + if (!duplicate) + { + Node::Dedis.push_back(entry); + } + } + + void Node::SendNodeList(Network::Address target) + { + if (target.IsSelf()) return; + + std::vector entries; + + for (auto entry : Node::Nodes) + { + if (entry.state != Node::STATE_INVALID) // Only send valid nodes, or shall we send invalid ones as well? + { + Node::AddressEntry thisAddress; + thisAddress.fromNetAddress(entry.address); + + entries.push_back(thisAddress); + } + + if (entries.size() >= 111) + { + std::string packet = "nodeNodeList\n"; + packet.append(reinterpret_cast(entries.data()), entries.size() * sizeof(Node::AddressEntry)); + + Network::SendRaw(target, packet); + + entries.clear(); + } + } + + std::string packet = "nodeNodeList\n"; + packet.append(reinterpret_cast(entries.data()), entries.size() * sizeof(Node::AddressEntry)); + + Network::SendRaw(target, packet); + } + + void Node::SendDediList(Network::Address target) + { + if (target.IsSelf()) return; + + std::vector entries; + + for (auto entry : Node::Dedis) + { + if (entry.state == Node::STATE_VALID) // Only send valid dedis + { + Node::AddressEntry thisAddress; + thisAddress.fromNetAddress(entry.address); + + entries.push_back(thisAddress); + } + } + + std::string packet = "nodeDediList\n"; + packet.append(reinterpret_cast(entries.data()), entries.size() * sizeof(Node::AddressEntry)); + + Network::SendRaw(target, packet); + } + + Node::Node() + { +#ifdef USE_NODE_STUFF + Assert_Size(Node::AddressEntry, 6); + + Dvar::OnInit([] () + { + Node::Nodes.clear(); + Node::LoadNodes(); + + Node::Dedis.clear(); + Node::LoadDedis(); + }); + + Network::Handle("nodeRequestLists", [] (Network::Address address, std::string data) + { + Logger::Print("Sending our lists to %s\n", address.GetString()); + + // Consider this a node for now! + //Node::AddNode(address, true); + + Node::SendNodeList(address); + //Node::SendDediList(address); + }); + + Network::Handle("nodeNodeList", [] (Network::Address address, std::string data) + { + if (data.size() % sizeof(Node::AddressEntry)) + { + Logger::Print("Received invalid node list from %s!\n", address.GetString()); + return; + } + + unsigned int size = (data.size() / sizeof(Node::AddressEntry)); + + Logger::Print("Received valid node list with %d entries from %s\n", size, address.GetString()); + + // Insert the node itself and mark it as valid + Node::AddNode(address, true); + + Node::AddressEntry* addresses = reinterpret_cast(const_cast(data.data())); + for (unsigned int i = 0; i < size; ++i) + { + Node::AddNode(addresses[i].toNetAddress()); + } + }); + + Network::Handle("nodeDediList", [] (Network::Address address, std::string data) + { + if (data.size() % sizeof(Node::AddressEntry)) + { + Logger::Print("Received invalid dedi list from %s!\n", address.GetString()); + return; + } + + unsigned int size = (data.size() / sizeof(Node::AddressEntry)); + + Logger::Print("Received valid dedi list with %d entries from %s\n", size, address.GetString()); + + // Insert the node and mark it as valid + Node::AddNode(address, true); + + Node::AddressEntry* addresses = reinterpret_cast(const_cast(data.data())); + for (unsigned int i = 0; i < size; ++i) + { + Node::AddDedi(addresses[i].toNetAddress()); + } + }); + + Dedicated::OnFrame([] () + { + int count = 0; + + // Send requests + for (auto &node : Node::Nodes) + { + // Frame limit + if (count >= 1) break; // Query only 1 node per frame (-> 3 packets sent per frame) + + if (node.state == Node::STATE_UNKNOWN || (/*node.state != Node::STATE_INVALID && */node.state != Node::STATE_QUERYING && (Game::Com_Milliseconds() - node.endTime) > (1000 * 30))) + { + count++; + + node.startTime = Game::Com_Milliseconds(); + node.endTime = 0; + node.state = Node::STATE_QUERYING; + + Logger::Print("Syncing with node %s...\n", node.address.GetString()); + + // Request new lists + Network::Send(node.address, "nodeRequestLists"); + + // Send our lists + Node::SendNodeList(node.address); + //Node::SendDediList(node.address); + } + } + + // Mark invalid nodes + for (auto &node : Node::Nodes) + { + if (node.state == Node::STATE_QUERYING && (Game::Com_Milliseconds() - node.startTime) > (1000 * 10)) + { + node.state = Node::STATE_INVALID; + node.endTime = Game::Com_Milliseconds(); + } + } + + count = 0; + }); + + Command::Add("listnodes", [] (Command::Params params) + { + Logger::Print("Nodes: %d\n", Node::Nodes.size()); + + for (auto node : Node::Nodes) + { + Logger::Print("%s\n", node.address.GetString()); + } + }); + + Command::Add("addnode", [](Command::Params params) + { + if (params.Length() < 2) return; + + Node::AddNode(Network::Address(params[1])); + }); +#endif + } + + Node::~Node() + { + Node::StoreNodes(); + Node::Nodes.clear(); + + Node::StoreDedis(); + Node::Dedis.clear(); + } +} diff --git a/src/Components/Modules/Node.hpp b/src/Components/Modules/Node.hpp new file mode 100644 index 00000000..e4733ab2 --- /dev/null +++ b/src/Components/Modules/Node.hpp @@ -0,0 +1,75 @@ +namespace Components +{ + class Node : public Component + { + public: + Node(); + ~Node(); + const char* GetName() { return "Node"; }; + + private: + enum EntryState + { + STATE_UNKNOWN, + STATE_QUERYING, + STATE_VALID, + STATE_INVALID + }; + + struct NodeEntry + { + Network::Address address; + EntryState state; + int startTime; + int endTime; + }; + + struct DediEntry + { + Network::Address address; + EntryState state; + int startTime; + int endTime; + }; + +#pragma pack(push, 1) + struct AddressEntry + { + Game::netIP_t ip; + unsigned short port; + + Network::Address toNetAddress() + { + Network::Address address; + + address.SetIP(this->ip); + address.SetPort(ntohs(this->port)); + address.SetType(Game::netadrtype_t::NA_IP); + + return address; + } + + void fromNetAddress(Network::Address address) + { + this->ip = address.GetIP(); + this->port = htons(address.GetPort()); + } + }; +#pragma pack(pop) + + static std::vector Nodes; + static std::vector Dedis; + + static void LoadNodes(); + static void LoadDedis(); + + static void StoreNodes(); + static void StoreDedis(); + + static void AddNode(Network::Address address, bool valid = false); + static void AddDedi(Network::Address address); + + static void SendNodeList(Network::Address target); + static void SendDediList(Network::Address target); + }; +}