More node stuff.

This commit is contained in:
momo5502 2016-01-28 21:37:48 +01:00
parent e79ac58f8e
commit b350c3774b
7 changed files with 202 additions and 47 deletions

View File

@ -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());

View File

@ -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();
}
}

View File

@ -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);

View File

@ -375,6 +375,7 @@ namespace Components
}
}
Node::ValidateDedi(address, info);
ServerList::Insert(address, info);
});
}

View File

@ -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();
}
}

View File

@ -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);
};
}

View File

@ -138,8 +138,12 @@ namespace Utils
void WriteFile(std::string file, std::string data)
{
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)