More node stuff.
This commit is contained in:
parent
e79ac58f8e
commit
b350c3774b
@ -51,6 +51,7 @@ namespace Components
|
|||||||
|
|
||||||
void Loader::Uninitialize()
|
void Loader::Uninitialize()
|
||||||
{
|
{
|
||||||
|
std::reverse(Loader::Components.begin(), Loader::Components.end());
|
||||||
for (auto component : Loader::Components)
|
for (auto component : Loader::Components)
|
||||||
{
|
{
|
||||||
Logger::Print("Unregistering component: %s\n", component->GetName());
|
Logger::Print("Unregistering component: %s\n", component->GetName());
|
||||||
|
@ -7,46 +7,50 @@ namespace Components
|
|||||||
|
|
||||||
void Node::LoadNodes()
|
void Node::LoadNodes()
|
||||||
{
|
{
|
||||||
std::string nodes = Utils::ReadFile("nodes.txt");
|
std::string nodes = Utils::ReadFile("players/nodes.dat");
|
||||||
auto list = Utils::Explode(nodes, '\n');
|
|
||||||
|
|
||||||
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(addresses[i].toNetAddress());
|
||||||
Node::AddNode(addr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::LoadDedis()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Node::StoreNodes()
|
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());
|
Node::AddressEntry thisAddress;
|
||||||
nodes.append("\n");
|
thisAddress.fromNetAddress(entry.address);
|
||||||
|
|
||||||
|
entries.push_back(thisAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::WriteFile("nodes.txt", nodes);
|
std::string nodeStream(reinterpret_cast<char*>(entries.data()), entries.size() * sizeof(Node::AddressEntry));
|
||||||
}
|
|
||||||
|
|
||||||
void Node::StoreDedis()
|
|
||||||
{
|
|
||||||
|
|
||||||
|
CreateDirectoryW(L"players", NULL);
|
||||||
|
Utils::WriteFile("players/nodes.dat", nodeStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::AddNode(Network::Address address, bool valid)
|
void Node::AddNode(Network::Address address, bool valid)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (address.IsSelf()) return;
|
||||||
|
#else
|
||||||
if (address.IsLocal() || address.IsSelf()) return;
|
if (address.IsLocal() || address.IsSelf()) return;
|
||||||
|
#endif
|
||||||
|
|
||||||
Node::NodeEntry entry;
|
Node::NodeEntry entry;
|
||||||
|
|
||||||
entry.startTime = 0;
|
entry.startTime = 0;
|
||||||
|
entry.lastHeartbeat = 0;
|
||||||
entry.endTime = (valid ? Game::Com_Milliseconds() : 0);
|
entry.endTime = (valid ? Game::Com_Milliseconds() : 0);
|
||||||
entry.state = (valid ? Node::STATE_VALID : Node::STATE_UNKNOWN);
|
entry.state = (valid ? Node::STATE_VALID : Node::STATE_UNKNOWN);
|
||||||
entry.address = address;
|
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;
|
Node::DediEntry entry;
|
||||||
|
|
||||||
@ -87,10 +91,16 @@ namespace Components
|
|||||||
|
|
||||||
// Search if we already know that node
|
// Search if we already know that node
|
||||||
bool duplicate = false;
|
bool duplicate = false;
|
||||||
for (auto ourEntry : Node::Nodes)
|
for (auto &ourEntry : Node::Dedis)
|
||||||
{
|
{
|
||||||
if (ourEntry.address == entry.address)
|
if (ourEntry.address == entry.address)
|
||||||
{
|
{
|
||||||
|
if (dirty)
|
||||||
|
{
|
||||||
|
ourEntry.endTime = Game::Com_Milliseconds();
|
||||||
|
ourEntry.state = Node::STATE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
duplicate = true;
|
duplicate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -159,6 +169,24 @@ namespace Components
|
|||||||
Network::SendRaw(target, packet);
|
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()
|
Node::Node()
|
||||||
{
|
{
|
||||||
//#ifdef USE_NODE_STUFF
|
//#ifdef USE_NODE_STUFF
|
||||||
@ -166,13 +194,22 @@ namespace Components
|
|||||||
|
|
||||||
Dvar::OnInit([] ()
|
Dvar::OnInit([] ()
|
||||||
{
|
{
|
||||||
|
Node::Dedis.clear();
|
||||||
Node::Nodes.clear();
|
Node::Nodes.clear();
|
||||||
Node::LoadNodes();
|
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)
|
Network::Handle("nodeRequestLists", [] (Network::Address address, std::string data)
|
||||||
{
|
{
|
||||||
Logger::Print("Sending our lists to %s\n", address.GetString());
|
Logger::Print("Sending our lists to %s\n", address.GetString());
|
||||||
@ -181,7 +218,7 @@ namespace Components
|
|||||||
//Node::AddNode(address, true);
|
//Node::AddNode(address, true);
|
||||||
|
|
||||||
Node::SendNodeList(address);
|
Node::SendNodeList(address);
|
||||||
//Node::SendDediList(address);
|
Node::SendDediList(address);
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::Handle("nodeNodeList", [] (Network::Address address, std::string data)
|
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([] ()
|
Dedicated::OnFrame([] ()
|
||||||
{
|
{
|
||||||
|
int heartbeatCount = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
// Send requests
|
// Send requests
|
||||||
for (auto &node : Node::Nodes)
|
for (auto &node : Node::Nodes)
|
||||||
{
|
{
|
||||||
// Frame limit
|
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))))
|
||||||
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++;
|
count++;
|
||||||
|
|
||||||
@ -253,18 +308,60 @@ namespace Components
|
|||||||
|
|
||||||
// Send our lists
|
// Send our lists
|
||||||
Node::SendNodeList(node.address);
|
Node::SendNodeList(node.address);
|
||||||
//Node::SendDediList(node.address);
|
Node::SendDediList(node.address);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Mark invalid nodes
|
if (node.state == Node::STATE_QUERYING && (Game::Com_Milliseconds() - node.startTime) > (NODE_QUERY_TIMEOUT))
|
||||||
for (auto &node : Node::Nodes)
|
|
||||||
{
|
|
||||||
if (node.state == Node::STATE_QUERYING && (Game::Com_Milliseconds() - node.startTime) > (1000 * 10))
|
|
||||||
{
|
{
|
||||||
node.state = Node::STATE_INVALID;
|
node.state = Node::STATE_INVALID;
|
||||||
node.endTime = Game::Com_Milliseconds();
|
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;
|
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)
|
Command::Add("addnode", [](Command::Params params)
|
||||||
{
|
{
|
||||||
if (params.Length() < 2) return;
|
if (params.Length() < 2) return;
|
||||||
@ -293,8 +400,5 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Node::StoreNodes();
|
Node::StoreNodes();
|
||||||
Node::Nodes.clear();
|
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
|
namespace Components
|
||||||
{
|
{
|
||||||
class Node : public Component
|
class Node : public Component
|
||||||
@ -7,6 +20,8 @@ namespace Components
|
|||||||
~Node();
|
~Node();
|
||||||
const char* GetName() { return "Node"; };
|
const char* GetName() { return "Node"; };
|
||||||
|
|
||||||
|
static void ValidateDedi(Network::Address address, Utils::InfoString info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum EntryState
|
enum EntryState
|
||||||
{
|
{
|
||||||
@ -22,11 +37,13 @@ namespace Components
|
|||||||
EntryState state;
|
EntryState state;
|
||||||
int startTime;
|
int startTime;
|
||||||
int endTime;
|
int endTime;
|
||||||
|
int lastHeartbeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DediEntry
|
struct DediEntry
|
||||||
{
|
{
|
||||||
Network::Address address;
|
Network::Address address;
|
||||||
|
std::string challenge;
|
||||||
EntryState state;
|
EntryState state;
|
||||||
int startTime;
|
int startTime;
|
||||||
int endTime;
|
int endTime;
|
||||||
@ -61,13 +78,10 @@ namespace Components
|
|||||||
static std::vector<DediEntry> Dedis;
|
static std::vector<DediEntry> Dedis;
|
||||||
|
|
||||||
static void LoadNodes();
|
static void LoadNodes();
|
||||||
static void LoadDedis();
|
|
||||||
|
|
||||||
static void StoreNodes();
|
static void StoreNodes();
|
||||||
static void StoreDedis();
|
|
||||||
|
|
||||||
static void AddNode(Network::Address address, bool valid = false);
|
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 SendNodeList(Network::Address target);
|
||||||
static void SendDediList(Network::Address target);
|
static void SendDediList(Network::Address target);
|
||||||
|
@ -375,6 +375,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node::ValidateDedi(address, info);
|
||||||
ServerList::Insert(address, info);
|
ServerList::Insert(address, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,29 @@
|
|||||||
|
|
||||||
namespace Components
|
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;
|
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()
|
void QuickPatch::UnlockStats()
|
||||||
{
|
{
|
||||||
Command::Execute("setPlayerData prestige 10");
|
Command::Execute("setPlayerData prestige 10");
|
||||||
@ -171,6 +188,8 @@ namespace Components
|
|||||||
Utils::Hook::Set<char*>(0x60BED2, "unskippablecinematic IW_logo\n");
|
Utils::Hook::Set<char*>(0x60BED2, "unskippablecinematic IW_logo\n");
|
||||||
Utils::Hook::Nop(0x60BEF6, 5); // Don't reset intro dvar
|
Utils::Hook::Nop(0x60BEF6, 5); // Don't reset intro dvar
|
||||||
|
|
||||||
|
Utils::Hook(0x4D4007, QuickPatch::ShutdownStub, HOOK_CALL).Install()->Quick();
|
||||||
|
|
||||||
// Rename stat file - TODO: beautify
|
// Rename stat file - TODO: beautify
|
||||||
Utils::Hook::SetString(0x71C048, "iw4x.stat");
|
Utils::Hook::SetString(0x71C048, "iw4x.stat");
|
||||||
|
|
||||||
@ -185,4 +204,9 @@ namespace Components
|
|||||||
QuickPatch::UnlockStats();
|
QuickPatch::UnlockStats();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QuickPatch::~QuickPatch()
|
||||||
|
{
|
||||||
|
QuickPatch::ShutdownCallbacks.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,19 @@ namespace Components
|
|||||||
class QuickPatch : public Component
|
class QuickPatch : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef void(*Callback)();
|
||||||
|
|
||||||
QuickPatch();
|
QuickPatch();
|
||||||
|
~QuickPatch();
|
||||||
const char* GetName() { return "QuickPatch"; };
|
const char* GetName() { return "QuickPatch"; };
|
||||||
|
|
||||||
static void UnlockStats();
|
static void UnlockStats();
|
||||||
|
static void OnShutdown(Callback callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static _int64* GetStatsID();
|
static std::vector<Callback> ShutdownCallbacks;
|
||||||
|
|
||||||
|
static int64_t* GetStatsID();
|
||||||
|
static void ShutdownStub(int channel, const char* message);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -138,8 +138,12 @@ namespace Utils
|
|||||||
void WriteFile(std::string file, std::string data)
|
void WriteFile(std::string file, std::string data)
|
||||||
{
|
{
|
||||||
std::ofstream stream(file, std::ios::binary);
|
std::ofstream stream(file, std::ios::binary);
|
||||||
stream.write(data.data(), data.size());
|
|
||||||
stream.close();
|
if (stream.is_open())
|
||||||
|
{
|
||||||
|
stream.write(data.data(), data.size());
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ReadFile(std::string file)
|
std::string ReadFile(std::string file)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user