More node stuff.
This commit is contained in:
parent
e79ac58f8e
commit
b350c3774b
@ -51,6 +51,7 @@ namespace Components
|
||||
|
||||
void Loader::Uninitialize()
|
||||
{
|
||||
std::reverse(Loader::Components.begin(), Loader::Components.end());
|
||||
for (auto component : Loader::Components)
|
||||
{
|
||||
Logger::Print("Unregistering component: %s\n", component->GetName());
|
||||
|
@ -7,46 +7,50 @@ namespace Components
|
||||
|
||||
void Node::LoadNodes()
|
||||
{
|
||||
std::string nodes = Utils::ReadFile("nodes.txt");
|
||||
auto list = Utils::Explode(nodes, '\n');
|
||||
std::string nodes = Utils::ReadFile("players/nodes.dat");
|
||||
|
||||
for (auto entry : list)
|
||||
// Invalid
|
||||
if (!nodes.size() || nodes.size() % 6) return;
|
||||
|
||||
unsigned int size = (nodes.size() / sizeof(Node::AddressEntry));
|
||||
|
||||
Node::AddressEntry* addresses = reinterpret_cast<Node::AddressEntry*>(const_cast<char*>(nodes.data()));
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
{
|
||||
Network::Address addr(entry);
|
||||
Node::AddNode(addr);
|
||||
Node::AddNode(addresses[i].toNetAddress());
|
||||
}
|
||||
}
|
||||
|
||||
void Node::LoadDedis()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Node::StoreNodes()
|
||||
{
|
||||
std::string nodes;
|
||||
std::vector<Node::AddressEntry> entries;
|
||||
|
||||
for (auto node : Node::Nodes)
|
||||
for (auto entry : Node::Nodes)
|
||||
{
|
||||
nodes.append(node.address.GetString());
|
||||
nodes.append("\n");
|
||||
Node::AddressEntry thisAddress;
|
||||
thisAddress.fromNetAddress(entry.address);
|
||||
|
||||
entries.push_back(thisAddress);
|
||||
}
|
||||
|
||||
Utils::WriteFile("nodes.txt", nodes);
|
||||
}
|
||||
|
||||
void Node::StoreDedis()
|
||||
{
|
||||
std::string nodeStream(reinterpret_cast<char*>(entries.data()), entries.size() * sizeof(Node::AddressEntry));
|
||||
|
||||
CreateDirectoryW(L"players", NULL);
|
||||
Utils::WriteFile("players/nodes.dat", nodeStream);
|
||||
}
|
||||
|
||||
void Node::AddNode(Network::Address address, bool valid)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (address.IsSelf()) return;
|
||||
#else
|
||||
if (address.IsLocal() || address.IsSelf()) return;
|
||||
#endif
|
||||
|
||||
Node::NodeEntry entry;
|
||||
|
||||
entry.startTime = 0;
|
||||
entry.lastHeartbeat = 0;
|
||||
entry.endTime = (valid ? Game::Com_Milliseconds() : 0);
|
||||
entry.state = (valid ? Node::STATE_VALID : Node::STATE_UNKNOWN);
|
||||
entry.address = address;
|
||||
@ -76,7 +80,7 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Node::AddDedi(Network::Address address)
|
||||
void Node::AddDedi(Network::Address address, bool dirty)
|
||||
{
|
||||
Node::DediEntry entry;
|
||||
|
||||
@ -87,10 +91,16 @@ namespace Components
|
||||
|
||||
// Search if we already know that node
|
||||
bool duplicate = false;
|
||||
for (auto ourEntry : Node::Nodes)
|
||||
for (auto &ourEntry : Node::Dedis)
|
||||
{
|
||||
if (ourEntry.address == entry.address)
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
ourEntry.endTime = Game::Com_Milliseconds();
|
||||
ourEntry.state = Node::STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
@ -159,6 +169,24 @@ namespace Components
|
||||
Network::SendRaw(target, packet);
|
||||
}
|
||||
|
||||
void Node::ValidateDedi(Network::Address address, Utils::InfoString info)
|
||||
{
|
||||
for (auto &dedi : Node::Dedis)
|
||||
{
|
||||
if (dedi.address == address)
|
||||
{
|
||||
dedi.state = (info.Get("challenge") == dedi.challenge ? Node::STATE_VALID : Node::STATE_INVALID);
|
||||
dedi.endTime = Game::Com_Milliseconds();
|
||||
|
||||
if (dedi.state == Node::STATE_VALID)
|
||||
{
|
||||
Logger::Print("Validated dedi %s\n", address.GetString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node::Node()
|
||||
{
|
||||
//#ifdef USE_NODE_STUFF
|
||||
@ -166,13 +194,22 @@ namespace Components
|
||||
|
||||
Dvar::OnInit([] ()
|
||||
{
|
||||
Node::Dedis.clear();
|
||||
Node::Nodes.clear();
|
||||
Node::LoadNodes();
|
||||
|
||||
Node::Dedis.clear();
|
||||
Node::LoadDedis();
|
||||
});
|
||||
|
||||
if (Dedicated::IsDedicated())
|
||||
{
|
||||
QuickPatch::OnShutdown([] ()
|
||||
{
|
||||
for (auto node : Node::Nodes)
|
||||
{
|
||||
Network::Send(node.address, "heartbeatDeadline\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Network::Handle("nodeRequestLists", [] (Network::Address address, std::string data)
|
||||
{
|
||||
Logger::Print("Sending our lists to %s\n", address.GetString());
|
||||
@ -181,7 +218,7 @@ namespace Components
|
||||
//Node::AddNode(address, true);
|
||||
|
||||
Node::SendNodeList(address);
|
||||
//Node::SendDediList(address);
|
||||
Node::SendDediList(address);
|
||||
});
|
||||
|
||||
Network::Handle("nodeNodeList", [] (Network::Address address, std::string data)
|
||||
@ -228,17 +265,35 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
Network::Handle("heartbeat", [] (Network::Address address, std::string data)
|
||||
{
|
||||
Logger::Print("Received heartbeat from %s\n", address.GetString());
|
||||
Node::AddDedi(address, true);
|
||||
});
|
||||
|
||||
Network::Handle("heartbeatDeadline", [] (Network::Address address, std::string data)
|
||||
{
|
||||
for (auto &dedi : Node::Dedis)
|
||||
{
|
||||
if (dedi.address == address)
|
||||
{
|
||||
Logger::Print("Dedi invalidation message received from %s\n", address.GetString());
|
||||
|
||||
dedi.state = Node::STATE_INVALID;
|
||||
dedi.endTime = Game::Com_Milliseconds();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Dedicated::OnFrame([] ()
|
||||
{
|
||||
int heartbeatCount = 0;
|
||||
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)))
|
||||
if (count < NODE_FRAME_QUERY_LIMIT && (node.state == Node::STATE_UNKNOWN || (/*node.state != Node::STATE_INVALID && */node.state != Node::STATE_QUERYING && (Game::Com_Milliseconds() - node.endTime) > (NODE_VALIDITY_EXPIRE))))
|
||||
{
|
||||
count++;
|
||||
|
||||
@ -253,18 +308,60 @@ namespace Components
|
||||
|
||||
// Send our lists
|
||||
Node::SendNodeList(node.address);
|
||||
//Node::SendDediList(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))
|
||||
if (node.state == Node::STATE_QUERYING && (Game::Com_Milliseconds() - node.startTime) > (NODE_QUERY_TIMEOUT))
|
||||
{
|
||||
node.state = Node::STATE_INVALID;
|
||||
node.endTime = Game::Com_Milliseconds();
|
||||
}
|
||||
|
||||
if (node.state == Node::STATE_VALID)
|
||||
{
|
||||
if (heartbeatCount < HEARTBEATS_FRAME_LIMIT && (!node.lastHeartbeat || (Game::Com_Milliseconds() - node.lastHeartbeat) > (HEARTBEAT_INTERVAL)))
|
||||
{
|
||||
heartbeatCount++;
|
||||
|
||||
Logger::Print("Sending heartbeat to node %s...\n", node.address.GetString());
|
||||
node.lastHeartbeat = Game::Com_Milliseconds();
|
||||
Network::Send(node.address, "heartbeat\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (auto &dedi : Node::Dedis)
|
||||
{
|
||||
if (count < DEDI_FRAME_QUERY_LIMIT && (dedi.state == Node::STATE_UNKNOWN || (/*node.state != Node::STATE_INVALID && */dedi.state != Node::STATE_QUERYING && (Game::Com_Milliseconds() - dedi.endTime) > (DEDI_VALIDITY_EXPIRE))))
|
||||
{
|
||||
count++;
|
||||
|
||||
dedi.startTime = Game::Com_Milliseconds();
|
||||
dedi.endTime = 0;
|
||||
dedi.challenge = Utils::VA("%d", dedi.startTime);
|
||||
dedi.state = Node::STATE_QUERYING;
|
||||
|
||||
Logger::Print("Verifying dedi %s...\n", dedi.address.GetString());
|
||||
|
||||
// Request new lists
|
||||
Network::Send(dedi.address, Utils::VA("getinfo %s\n", dedi.challenge.data()));
|
||||
}
|
||||
|
||||
// No query response
|
||||
if (dedi.state == Node::STATE_QUERYING && (Game::Com_Milliseconds() - dedi.startTime) > (DEDI_QUERY_TIMEOUT))
|
||||
{
|
||||
dedi.state = Node::STATE_INVALID;
|
||||
dedi.endTime = Game::Com_Milliseconds();
|
||||
}
|
||||
|
||||
// Lack of heartbeats
|
||||
if (dedi.state == Node::STATE_VALID && (Game::Com_Milliseconds() - dedi.startTime) > (HEARTBEAT_DEADLINE))
|
||||
{
|
||||
Logger::Print("Invalidating dedi %s\n", dedi.address.GetString());
|
||||
dedi.state = Node::STATE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
@ -280,6 +377,16 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("listdedis", [] (Command::Params params)
|
||||
{
|
||||
Logger::Print("Dedi: %d\n", Node::Dedis.size());
|
||||
|
||||
for (auto dedi : Node::Dedis)
|
||||
{
|
||||
Logger::Print("%s\n", dedi.address.GetString());
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("addnode", [](Command::Params params)
|
||||
{
|
||||
if (params.Length() < 2) return;
|
||||
@ -293,8 +400,5 @@ namespace Components
|
||||
{
|
||||
Node::StoreNodes();
|
||||
Node::Nodes.clear();
|
||||
|
||||
Node::StoreDedis();
|
||||
Node::Dedis.clear();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,16 @@
|
||||
#define HEARTBEAT_DEADLINE 1000 * 3 * 10 // Invalidate servers after 10 minutes without heartbeat
|
||||
#define HEARTBEAT_INTERVAL 1000 * 10 * 1 // Send heartbeats to each node every 3 minutes
|
||||
|
||||
#define NODE_VALIDITY_EXPIRE 1000 * 60 * 2 // Revalidate nodes after 2 minutes
|
||||
#define DEDI_VALIDITY_EXPIRE 1000 * 60 * 2 // Revalidate dedis after 2 minutes
|
||||
|
||||
#define NODE_QUERY_TIMEOUT 1000 * 30 * 1 // Invalidate nodes after 30 seconds without query response
|
||||
#define DEDI_QUERY_TIMEOUT 1000 * 10 * 1 // Invalidate dedis after 10 seconds without query response
|
||||
|
||||
#define HEARTBEATS_FRAME_LIMIT 1 // Limit of heartbeats sent to nodes per frame
|
||||
#define NODE_FRAME_QUERY_LIMIT 1 // Limit of nodes to be queried per frame
|
||||
#define DEDI_FRAME_QUERY_LIMIT 1 // Limit of dedis to be queried per frame
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Node : public Component
|
||||
@ -7,6 +20,8 @@ namespace Components
|
||||
~Node();
|
||||
const char* GetName() { return "Node"; };
|
||||
|
||||
static void ValidateDedi(Network::Address address, Utils::InfoString info);
|
||||
|
||||
private:
|
||||
enum EntryState
|
||||
{
|
||||
@ -22,11 +37,13 @@ namespace Components
|
||||
EntryState state;
|
||||
int startTime;
|
||||
int endTime;
|
||||
int lastHeartbeat;
|
||||
};
|
||||
|
||||
struct DediEntry
|
||||
{
|
||||
Network::Address address;
|
||||
std::string challenge;
|
||||
EntryState state;
|
||||
int startTime;
|
||||
int endTime;
|
||||
@ -61,13 +78,10 @@ namespace Components
|
||||
static std::vector<DediEntry> 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 AddDedi(Network::Address address, bool dirty = false);
|
||||
|
||||
static void SendNodeList(Network::Address target);
|
||||
static void SendDediList(Network::Address target);
|
||||
|
@ -375,6 +375,7 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
Node::ValidateDedi(address, info);
|
||||
ServerList::Insert(address, info);
|
||||
});
|
||||
}
|
||||
|
@ -2,12 +2,29 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
__int64* QuickPatch::GetStatsID()
|
||||
std::vector<QuickPatch::Callback> QuickPatch::ShutdownCallbacks;
|
||||
|
||||
int64_t* QuickPatch::GetStatsID()
|
||||
{
|
||||
static __int64 id = 0x110000100001337;
|
||||
static int64_t id = 0x110000100001337;
|
||||
return &id;
|
||||
}
|
||||
|
||||
void QuickPatch::OnShutdown(QuickPatch::Callback callback)
|
||||
{
|
||||
QuickPatch::ShutdownCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void QuickPatch::ShutdownStub(int channel, const char* message)
|
||||
{
|
||||
Game::Com_Printf(0, message);
|
||||
|
||||
for (auto callback : QuickPatch::ShutdownCallbacks)
|
||||
{
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
|
||||
void QuickPatch::UnlockStats()
|
||||
{
|
||||
Command::Execute("setPlayerData prestige 10");
|
||||
@ -171,6 +188,8 @@ namespace Components
|
||||
Utils::Hook::Set<char*>(0x60BED2, "unskippablecinematic IW_logo\n");
|
||||
Utils::Hook::Nop(0x60BEF6, 5); // Don't reset intro dvar
|
||||
|
||||
Utils::Hook(0x4D4007, QuickPatch::ShutdownStub, HOOK_CALL).Install()->Quick();
|
||||
|
||||
// Rename stat file - TODO: beautify
|
||||
Utils::Hook::SetString(0x71C048, "iw4x.stat");
|
||||
|
||||
@ -185,4 +204,9 @@ namespace Components
|
||||
QuickPatch::UnlockStats();
|
||||
});
|
||||
}
|
||||
|
||||
QuickPatch::~QuickPatch()
|
||||
{
|
||||
QuickPatch::ShutdownCallbacks.clear();
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,19 @@ namespace Components
|
||||
class QuickPatch : public Component
|
||||
{
|
||||
public:
|
||||
typedef void(*Callback)();
|
||||
|
||||
QuickPatch();
|
||||
~QuickPatch();
|
||||
const char* GetName() { return "QuickPatch"; };
|
||||
|
||||
static void UnlockStats();
|
||||
static void OnShutdown(Callback callback);
|
||||
|
||||
private:
|
||||
static _int64* GetStatsID();
|
||||
static std::vector<Callback> ShutdownCallbacks;
|
||||
|
||||
static int64_t* GetStatsID();
|
||||
static void ShutdownStub(int channel, const char* message);
|
||||
};
|
||||
}
|
||||
|
@ -138,9 +138,13 @@ namespace Utils
|
||||
void WriteFile(std::string file, std::string data)
|
||||
{
|
||||
std::ofstream stream(file, std::ios::binary);
|
||||
|
||||
if (stream.is_open())
|
||||
{
|
||||
stream.write(data.data(), data.size());
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReadFile(std::string file)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user