summaryrefslogtreecommitdiff
path: root/Runtime/Network/MasterServerInterface.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Network/MasterServerInterface.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Network/MasterServerInterface.cpp')
-rw-r--r--Runtime/Network/MasterServerInterface.cpp692
1 files changed, 692 insertions, 0 deletions
diff --git a/Runtime/Network/MasterServerInterface.cpp b/Runtime/Network/MasterServerInterface.cpp
new file mode 100644
index 0000000..8ce20d7
--- /dev/null
+++ b/Runtime/Network/MasterServerInterface.cpp
@@ -0,0 +1,692 @@
+#include "UnityPrefix.h"
+#include "MasterServerInterface.h"
+
+#if ENABLE_NETWORK
+#include "External/RakNet/builds/include/TableSerializer.h"
+#include "External/RakNet/builds/include/BitStream.h"
+#include "External/RakNet/builds/include/StringCompressor.h"
+#include "External/RakNet/builds/include/DS_Table.h"
+#include "External/RakNet/builds/include/RakNetworkFactory.h"
+#include "External/RakNet/builds/include/SocketLayer.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "NetworkManager.h"
+#include <time.h>
+#include "NetworkUtility.h"
+
+// Future todos
+// TODO: ATM there is a 200 ms delay on disconnects. It "could" be possible that more than 200 ms pass before a network action is completed
+// maybe this should be done only after we make sure the operation is done.
+// NOTE: Row IDs are sent to clients when they do not have a row ID (first reg) or when the master server has restarted (and forgotten everything).
+// The problem is that updates from the client are sent with the old row ID which is not found during lookup and a new one sent to the client
+// The client never receives this new row ID because the connection is always closed after sending updates. The simples solution is to
+// keep client connections persistent for clients running hosts. Clients which just query for the host list (game clients) disconnect immediately.
+
+namespace {
+ const int kMasterServerPort = 23466;
+ const time_t kMaxUpdateInterval = 2;
+}
+
+MasterServerInterface::MasterServerInterface(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_Peer = RakNetworkFactory::GetRakPeerInterface();
+ m_GameType = "";
+ m_HostName = "";
+ m_PendingRegister = false;
+ m_PendingHostUpdate = false;
+ m_PendingQuery = false;
+ m_RowID = -1;
+ m_LastHostUpdateTime = 0;
+ m_Registered = false;
+ m_MasterServerID.binaryAddress = 0;
+ m_MasterServerID.port = kMasterServerPort;
+ m_Version[0]=2; m_Version[1]=0; m_Version[2]=0;
+ m_UpdateRate = 10;
+ m_IsDedicatedServer = false;
+ time(&m_ShutdownTimer);
+
+ m_HostDatabaseClient = new LightweightDatabaseClient;
+
+ // Create the cell update that will be reused for storing last update
+ strcpy(m_LastUpdate[0].columnName,"NAT");
+ m_LastUpdate[0].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[0].cellValue.Set(0);
+ strcpy(m_LastUpdate[1].columnName,"Game name");
+ m_LastUpdate[1].columnType = DataStructures::Table::STRING;
+ m_LastUpdate[1].cellValue.Set(0);
+ strcpy(m_LastUpdate[2].columnName,"Connected players");
+ m_LastUpdate[2].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[2].cellValue.Set(0);
+ strcpy(m_LastUpdate[3].columnName,"Player limit");
+ m_LastUpdate[3].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[3].cellValue.Set(0);
+ strcpy(m_LastUpdate[4].columnName,"Password protected");
+ m_LastUpdate[4].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[4].cellValue.Set(0);
+ strcpy(m_LastUpdate[5].columnName,"IP address");
+ m_LastUpdate[5].columnType = DataStructures::Table::BINARY;
+ m_LastUpdate[5].cellValue.Set(NULL, 0);
+ strcpy(m_LastUpdate[6].columnName,"Port");
+ m_LastUpdate[6].columnType = DataStructures::Table::NUMERIC;
+ m_LastUpdate[6].cellValue.Set(0);
+ strcpy(m_LastUpdate[7].columnName,"Comment");
+ m_LastUpdate[7].columnType = DataStructures::Table::STRING;
+ m_LastUpdate[7].cellValue.Set(0);
+}
+
+MasterServerInterface::~MasterServerInterface()
+{
+ delete m_HostDatabaseClient;
+ m_HostDatabaseClient = NULL;
+ RakNetworkFactory::DestroyRakPeerInterface(m_Peer);
+ m_Peer = NULL;
+}
+
+void MasterServerInterface::NetworkOnApplicationQuit()
+{
+ m_Peer->Shutdown(100);
+ m_HostList.clear();
+ // Reset to default values
+ m_MasterServerID.binaryAddress = 0;
+ m_MasterServerID.port = kMasterServerPort;
+ m_GameType = "";
+ m_HostName = "";
+ m_HostComment = "";
+ m_PendingRegister = false;
+ m_PendingHostUpdate = false;
+ m_PendingQuery = false;
+ m_RowID = -1;
+ m_Registered = false;
+ m_UpdateRate = 10;
+ m_IsDedicatedServer = false;
+}
+
+// Resolve the master server address if it is invalid
+void MasterServerInterface::ResolveMasterServerAddress()
+{
+ ResolveAddress(m_MasterServerID, "masterserver.unity3d.com", "masterserverbeta.unity3d.com",
+ "Cannot resolve master server address, you must be connected to the internet before using it or set the address to something accessible to you.");
+}
+
+void MasterServerInterface::ClientConnect()
+{
+ ResolveMasterServerAddress();
+
+ SocketDescriptor sd(0,0);
+ if (!m_Peer->Startup(1, 30, &sd, 1))
+ {
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kFailedToCreatedSocketOrThread);
+ }
+ m_Peer->AttachPlugin(&m_DatabaseClient);
+ if (!m_Peer->Connect(m_MasterServerID.ToString(false), m_MasterServerID.port, 0, 0))
+ {
+ if (m_Peer->GetMaximumNumberOfPeers() >= m_Peer->NumberOfConnections())
+ {
+ ErrorString("Internal error while connecting to master server. Too many connected peers.");
+ }
+ else
+ {
+ ErrorString("Internal error while attempting to connect to master server.");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kIncorrectParameters);
+ }
+ }
+}
+
+bool MasterServerInterface::CheckServerConnection()
+{
+ ResolveMasterServerAddress();
+
+ if (!GetNetworkManager().GetPeer()->IsConnected(m_MasterServerID))
+ {
+ ServerConnect();
+ return false;
+ }
+ if (!GetNetworkManager().GetPeer()->IsActive())
+ {
+ ServerConnect();
+ return false;
+ }
+ return true;
+}
+
+void MasterServerInterface::ServerConnect()
+{
+ if (!GetNetworkManager().GetPeer()->Connect(m_MasterServerID.ToString(false), m_MasterServerID.port, 0, 0))
+ {
+ ErrorString("Internal error while attempting to connect to master server\n");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kInternalDirectConnectFailed);
+ }
+ NetworkInfo(NULL, "Attempting to connect to master server at %s:%d", m_MasterServerID.ToString(false), m_MasterServerID.port);
+ m_PendingRegister = true;
+}
+
+void MasterServerInterface::ProcessPacket(Packet *packet)
+{
+ switch(packet->data[0])
+ {
+ // Disconnect and connection lost only occurs for clients requesting host info, not servers registering their info
+ case ID_DISCONNECTION_NOTIFICATION:
+ {
+ NetworkInfo(NULL, "Disconnected from master server");
+ SendToAllNetworkViews(kDisconnectedFromMasterServer, ID_DISCONNECTION_NOTIFICATION);
+ m_PendingQuery = false;
+ break;
+ }
+ case ID_CONNECTION_LOST:
+ {
+ // If connection was lost with master server we should re-register the host. If running as client do nothing.
+ if (GetNetworkManager().IsServer())
+ {
+ NetworkInfo(NULL, "Lost connection to master server, reconnecting and resending host info");
+ ResetHostState();
+ SendHostUpdate();
+ }
+ else
+ {
+ ErrorString("Connection with master server lost");
+ SendToAllNetworkViews(kDisconnectedFromMasterServer, ID_CONNECTION_LOST);
+ m_PendingQuery = false;
+ }
+ break;
+ }
+ case ID_CONNECTION_BANNED:
+ {
+ ErrorString("Temporarily banned from the master server");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, ID_CONNECTION_BANNED);
+ break;
+ }
+ case ID_CONNECTION_REQUEST_ACCEPTED:
+ {
+ NetworkInfo(NULL, "Connected to master server at %s", packet->systemAddress.ToString());
+ if (m_PendingRegister)
+ {
+ m_PendingRegister = false;
+ RegisterHost(m_GameType, m_HostName, m_HostComment);
+ }
+ if (m_PendingQuery)
+ {
+ m_PendingQuery = false;
+ QueryHostList(m_GameType);
+ }
+ if (m_PendingHostUpdate)
+ {
+ m_PendingHostUpdate = false;
+ SendHostUpdate();
+ }
+ break;
+ }
+ case ID_ALREADY_CONNECTED:
+ {
+ NetworkError(NULL, "Already connected to the master server, the server probably hasn't cleaned up because of an abrupt disconnection.");
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, ID_ALREADY_CONNECTED);
+ m_PendingQuery = false;
+ break;
+ }
+ case ID_CONNECTION_ATTEMPT_FAILED:
+ {
+ ErrorString(Format("Failed to connect to master server at %s", packet->systemAddress.ToString()));
+ SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, ID_CONNECTION_ATTEMPT_FAILED);
+ ResetHostState();
+ break;
+ }
+ // TODO: atm the server does not send this during client lookups, but maybe it should (as all tables are dynamically created this will never be returned to hosts)
+ case ID_DATABASE_UNKNOWN_TABLE:
+ {
+ ErrorString("Unkown game type");
+ // This uses NetworkConnectionError enums, and this status message doesn't belong in there
+ //SendToAllNetworkViews(kMasterServerConnectionAttemptFailed, kUnkownGameType);
+ break;
+ }
+ // This should never occur as we don't use db passwords directly
+ case ID_DATABASE_INCORRECT_PASSWORD:
+ {
+ ErrorString("Incorrect master server password");
+ break;
+ }
+ case ID_DATABASE_QUERY_REPLY:
+ {
+ NetworkInfo(NULL, "Incoming host list query response from master server.");
+
+ DataStructures::Table table;
+ if (TableSerializer::DeserializeTable(packet->data+sizeof(MessageID), packet->length-sizeof(MessageID), &table))
+ {
+ m_HostList.clear();
+ DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur = table.GetListHead();
+ while (cur)
+ {
+ for (int i=0; i < (unsigned)cur->size; i++)
+ {
+ // NOTE: The first three cell values are the systemId (binary), last ping response time and next ping
+ // send time(numerics)
+ DataStructures::List<DataStructures::Table::Cell*> cells = cur->data[i]->cells;
+ HostData data;
+ if (cells[5]->c != NULL &&
+ (int)cells[9]->i != 0 &&
+ cells[9]->c != NULL &&
+ (int)cells[10]->i != 0 &&
+ cells[12]->c != NULL)
+ {
+ data.useNat = (int)cells[4]->i;
+ data.gameType = m_GameType;
+ data.gameName = cells[5]->c;
+ data.connectedPlayers = (int)cells[6]->i;
+ data.playerLimit = (int)cells[7]->i;
+ data.passwordProtected = (int)cells[8]->i;
+ data.guid = cells[12]->c;
+
+ int ipCount = int(cells[9]->i / 16);
+ // If the size of the data is not a factor of 16 then something has gone wrong (IP addresses have size 16)
+ if (((int)cells[9]->i) % 16 == 0)
+ {
+ for (int ip=0; ip < ipCount; ip++)
+ {
+ const char* ipData = cells[9]->c + ip * 16;
+ if( ipData[0] == 0 ) break;
+ data.IP.push_back( ipData );
+ }
+ }
+ else
+ {
+ ErrorString(Format("Malformed data inside IP information packet. Size was %f", cells[8]->i));
+ }
+ data.port = int(cells[10]->i);
+ if (cells[11]->c != NULL)
+ data.comment = cells[11]->c;
+ else
+ data.comment = "";
+ m_HostList.push_back(data);
+ }
+ else
+ {
+ ErrorString("Received malformed data in the host list from the master server\n");
+ }
+ }
+ cur=cur->next;
+ }
+ }
+ else
+ {
+ m_HostList.clear();
+ }
+ SendToAllNetworkViews(kMasterServerEvent, kHostListReceived);
+ break;
+ }
+ case ID_DATABASE_ROWID:
+ {
+ unsigned int rowID;
+ RakNet::BitStream stream;
+ stream.Write((char*)packet->data, packet->length);
+ stream.IgnoreBits(8);
+ stream.Read(rowID);
+
+ NetworkInfo(NULL, "Received identifier %u from master server", rowID);
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationSucceeded);
+ m_RowID = rowID;
+
+ break;
+ }
+ case ID_MASTERSERVER_REDIRECT:
+ {
+ SystemAddress newMaster, newFacilitator;
+ RakNet::BitStream b(packet->data, packet->length, false);
+ b.IgnoreBits(8);
+ b.Read(newMaster);
+ b.Read(newFacilitator);
+
+ GetNetworkManager().SwapFacilitatorID(newFacilitator);
+ SystemAddress oldMasterServer = m_MasterServerID;
+ GetNetworkManager().SetOldMasterServerAddress(oldMasterServer);
+ m_MasterServerID = newMaster;
+ ResetHostState();
+
+ if (GetNetworkManager().IsServer())
+ {
+ GetNetworkManager().GetPeer()->CloseConnection(oldMasterServer, true);
+ NetworkInfo(NULL, "Redirecting master server host updates to %s", newMaster.ToString());
+ NetworkInfo(NULL, "Changing facilitator location to %s", newFacilitator.ToString());
+ SendHostUpdate();
+ }
+ else
+ {
+ NetworkInfo(NULL, "Redirecting master server host list queries to %s", newMaster.ToString());
+ NetworkInfo(NULL, "Changing facilitator location to %s", newFacilitator.ToString());
+ Disconnect();
+ QueryHostList();
+ }
+ break;
+ }
+ case ID_MASTERSERVER_MSG:
+ {
+ int msgLength;
+ RakNet::BitStream b(packet->data, packet->length, false);
+ b.IgnoreBits(8);
+ b.Read(msgLength);
+ if (msgLength > 0)
+ {
+ char* msg = new char[msgLength];
+ b.Read(msg, msgLength);
+ LogString(Format("Message from master server: %s", msg));
+ delete[] msg;
+ }
+ break;
+ }
+ default:
+ {
+ NetworkError(NULL, "Unknown message from master server (%s) %d", packet->systemAddress.ToString(), packet->data[0]);
+ break;
+ }
+ }
+}
+
+void MasterServerInterface::NetworkUpdate()
+{
+ if (!m_Peer)
+ return;
+
+ // Send heartbeat if running as server, only send if running as server and we have already registered (m_HostName set)
+ if (m_UpdateRate > 0 && m_Registered)
+ {
+ if ((time(0) - m_LastHostUpdateTime > m_UpdateRate) && m_HostName.size() > 1 && !m_PendingRegister)
+ {
+ SendHostUpdate();
+ }
+ }
+
+ if (!m_Peer->IsActive())
+ return;
+
+ // If not registering or already registered, this is a client, then check for shutdown timeout
+ if (!m_Registered && !m_PendingRegister && time(0) > m_ShutdownTimer + 20)
+ {
+ // Use a short delay, its not that bad if the disconnect notification never arrives at master server
+ m_Peer->Shutdown(50, 0);
+ }
+
+ Packet *p;
+ p=m_Peer->Receive();
+ while (p)
+ {
+ ProcessPacket(p);
+ m_Peer->DeallocatePacket(p);
+ p=m_Peer->Receive();
+ }
+}
+
+void MasterServerInterface::QueryHostList()
+{
+ QueryHostList(m_GameType);
+}
+
+void MasterServerInterface::QueryHostList(string gameType)
+{
+ time(&m_ShutdownTimer);
+
+ // Wait for previous query to clear
+ if (m_PendingQuery) return;
+
+ if (gameType.empty())
+ {
+ ErrorString("Empty game type given in QueryHostList(), aborting query.");
+ return;
+ }
+ m_GameType = gameType;
+
+ ResolveMasterServerAddress();
+
+ if (m_Peer == NULL)
+ {
+ ClientConnect();
+ m_PendingQuery = true;
+ return;
+ }
+ else if (!m_Peer->IsActive())
+ {
+ ClientConnect();
+ m_PendingQuery = true;
+ return;
+ }
+ else if (!m_Peer->IsConnected(m_MasterServerID))
+ {
+ ClientConnect();
+ m_PendingQuery = true;
+ return;
+ }
+
+ m_DatabaseClient.QueryTable(m_Version, gameType.c_str(), 0, 0, 0, 0, 0, 0, 0, m_MasterServerID, false);
+// LogString("Sent host query to master server");
+ // Disconnect after the list has arrived
+}
+
+void MasterServerInterface::ClearHostList()
+{
+ m_HostList.clear();
+}
+
+bool MasterServerInterface::PopulateUpdate()
+{
+ return PopulateUpdate(m_HostName, m_HostComment);
+}
+
+bool MasterServerInterface::PopulateUpdate(string gameName, string comment)
+{
+ // TODO: The function inside GetIPs uses char arrays which are pre-allocated. If it returns a full array then it is possible there
+ // are more IP addresses, in which case it should get a larger char array to use.
+ char ips[10][16];
+ int size = GetIPs(ips)*16;
+ if (size == 0)
+ ErrorString("Could not retrieve internal IP address. Host registration failed.");
+
+ bool changed = false;
+
+ if (((int)m_LastUpdate[0].cellValue.i) != static_cast<int>(GetNetworkManager().GetUseNat()))
+ changed = true;
+
+ if (((int)m_LastUpdate[1].cellValue.i) != 0 && changed != true)
+ {
+ if (strcmp(m_LastUpdate[1].cellValue.c,gameName.c_str()) != 0)
+ {
+ changed = true;
+ m_LastUpdate[1].cellValue.Clear();
+ m_LastUpdate[1].cellValue.Set(const_cast<char*>(gameName.c_str()));
+ }
+ }
+ else
+ {
+ changed = true;
+ }
+
+ //printf_console("connCount: Comparing %d and %d\n", intCell, GetNetworkManager().GetConnectionCount());
+ if (((int)m_LastUpdate[2].cellValue.i) != (GetNetworkManager().GetConnectionCount() + static_cast<int>(!m_IsDedicatedServer)) && changed != true)
+ changed = true;
+ //printf_console("maxCount: Comparing %d and %d\n", intCell, GetNetworkManager().GetMaxConnections());
+ if (((int)m_LastUpdate[3].cellValue.i) != GetNetworkManager().GetMaxConnections() + static_cast<int>(!m_IsDedicatedServer) && changed != true)
+ changed = true;
+ //printf_console("password: Comparing %d and %d\n", intCell, GetNetworkManager().IsPasswordProtected());
+ if (((int)m_LastUpdate[4].cellValue.i) != static_cast<int>(GetNetworkManager().IsPasswordProtected()) && changed != true)
+ changed = true;
+
+ if (((int)m_LastUpdate[5].cellValue.i) != 0 && changed != true)
+ {
+ /*printf_console("IPs: Comparing size %d and %d\n", intCell, size);
+ for (int i=0; i < intCell; i++)
+ printf_console("%x", tmpIPs[i]);
+ printf_console(" ");
+ for (int i=0; i < size; i++)
+ printf_console("%x", static_cast<char*>(ips[0])[i]);
+ printf_console("\n");*/
+ if (m_LastUpdate[5].cellValue.i != size)
+ changed = true;
+ else if (memcmp(m_LastUpdate[5].cellValue.c, ips, size) != 0)
+ changed = true;
+ }
+ else
+ {
+ changed = true;
+ }
+
+ //printf_console("port: Comparing %d and %d\n", intCell, GetNetworkManager().GetPort());
+ if (((int)m_LastUpdate[6].cellValue.i) != GetNetworkManager().GetPort() && changed != true)
+ changed = true;
+
+ if (((int)m_LastUpdate[7].cellValue.i) != 0 && changed != true)
+ {
+ //printf_console("comment: Comparing %s and %s\n", tmpComment, comment.c_str());
+ if (strcmp(m_LastUpdate[7].cellValue.c, comment.c_str()) != 0)
+ changed = true;
+ }
+ else
+ {
+ changed = true;
+ }
+
+ if (changed)
+ {
+ for (int i = 0; i < CELL_COUNT; i++)
+ m_LastUpdate[i].cellValue.Clear();
+ m_LastUpdate[0].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[0].cellValue.Set(GetNetworkManager().GetUseNat());
+ m_LastUpdate[1].columnType=DataStructures::Table::STRING;
+ m_LastUpdate[1].cellValue.Set(const_cast<char*>(gameName.c_str()));
+ m_LastUpdate[2].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[2].cellValue.Set(GetNetworkManager().GetConnectionCount() + static_cast<int>(!m_IsDedicatedServer));
+ m_LastUpdate[3].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[3].cellValue.Set(GetNetworkManager().GetMaxConnections() + static_cast<int>(!m_IsDedicatedServer));
+ m_LastUpdate[4].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[4].cellValue.Set(static_cast<int>(GetNetworkManager().IsPasswordProtected()));
+ m_LastUpdate[5].columnType=DataStructures::Table::BINARY;
+ m_LastUpdate[5].cellValue.Set((char*)ips, size);
+ m_LastUpdate[6].columnType=DataStructures::Table::NUMERIC;
+ m_LastUpdate[6].cellValue.Set(GetNetworkManager().GetPort());
+ m_LastUpdate[7].columnType=DataStructures::Table::STRING;
+ m_LastUpdate[7].cellValue.Set(const_cast<char*>(comment.c_str()));
+ }
+
+ return changed;
+}
+
+void MasterServerInterface::RegisterHost(string gameType, string gameName, string comment)
+{
+ // Wait until pending registrations have cleared or that a certain interval has passed (don't want to hammer the server)
+ if (m_PendingRegister || m_LastHostUpdateTime > time(0) - kMaxUpdateInterval)
+ return;
+
+ if (gameType.empty())
+ {
+ ErrorString("Empty game type given during host registration, aborting");
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationFailedGameName);
+ return;
+ }
+ if (gameName.empty())
+ {
+ ErrorString("Empty game name given during host registration, aborting");
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationFailedGameType);
+ return;
+ }
+ if (GetNetworkManager().GetPort() == 0)
+ {
+ ErrorString("It's not possible to register a host until it is running.");
+ SendToAllNetworkViews(kMasterServerEvent, kRegistrationFailedNoServer);
+ return;
+ }
+
+ m_GameType = gameType;
+ m_HostName = gameName;
+ m_HostComment = comment;
+
+ GetNetworkManager().GetPeer()->AttachPlugin(m_HostDatabaseClient);
+
+ if (!CheckServerConnection())
+ return;
+
+ PopulateUpdate();
+
+ m_LastHostUpdateTime = time(0);
+
+ m_HostDatabaseClient->UpdateRow(m_Version, gameType.c_str(), 0, RUM_UPDATE_OR_ADD_ROW, false, 0, m_LastUpdate, CELL_COUNT, m_MasterServerID, false );
+ NetworkLog(NULL, "Sent host registration to master server, registering a %sNAT assisted game as\n \"%s\", %d, %d, %s, \"%s\"",
+ (GetNetworkManager().GetUseNat()) ? "" : "non-",
+ gameName.c_str(),
+ GetNetworkManager().GetConnectionCount() + static_cast<int>(!m_IsDedicatedServer),
+ GetNetworkManager().GetMaxConnections() + static_cast<int>(!m_IsDedicatedServer),
+ (GetNetworkManager().IsPasswordProtected()) ? "password protected" : "not password protected",
+ comment.c_str());
+
+ m_Registered = true;
+}
+
+// Uses the game server peer
+void MasterServerInterface::SendHostUpdate()
+{
+ // Wait until pending host updates are finished
+ if (m_PendingHostUpdate)
+ {
+ NetworkInfo(NULL, "Still waiting for a master server reponse to another host update, ignoring this update.");
+ return;
+ }
+
+ if (!CheckServerConnection())
+ return;
+
+ if (!PopulateUpdate())
+ return;
+
+ m_LastHostUpdateTime = time(0);
+
+ if (m_RowID == -1)
+ {
+ m_HostDatabaseClient->UpdateRow(m_Version, m_GameType.c_str(), 0, RUM_UPDATE_OR_ADD_ROW, false, 0, m_LastUpdate, CELL_COUNT, m_MasterServerID, false );
+ NetworkInfo(NULL, "Sent new host update to master server");
+ }
+ else
+ {
+ m_HostDatabaseClient->UpdateRow(m_Version, m_GameType.c_str(), 0, RUM_UPDATE_OR_ADD_ROW, true, m_RowID, m_LastUpdate, CELL_COUNT, m_MasterServerID, false );
+ NetworkInfo(NULL, "Sent host update to master server with identifier %d", m_RowID);
+ }
+
+ m_Registered = true;
+}
+
+void MasterServerInterface::Disconnect()
+{
+ m_Peer->Shutdown(200);
+ m_Peer->DetachPlugin(&m_DatabaseClient);
+}
+
+// Uses the game server peer
+void MasterServerInterface::UnregisterHost()
+{
+ if (GetNetworkManagerPtr())
+ {
+ if (GetNetworkManager().GetPeer()->IsConnected(m_MasterServerID))
+ m_HostDatabaseClient->RemoveRow(m_GameType.c_str(), 0, m_RowID, m_MasterServerID, false);
+ // Always detach DB plugin when running as a server, nothing will happen if it's not attached
+ if (GetNetworkManager().IsServer())
+ GetNetworkManager().GetPeer()->DetachPlugin(m_HostDatabaseClient);
+ }
+
+ // Reset some local variables
+ m_RowID = -1;
+ m_GameType = "";
+ m_HostName = "";
+ m_HostComment = "";
+ m_Registered = false;
+}
+
+std::vector<HostData> MasterServerInterface::PollHostList()
+{
+ return m_HostList;
+}
+
+void MasterServerInterface::ResetHostState()
+{
+ m_PendingRegister = false;
+ m_PendingHostUpdate = false;
+ m_Registered = false;
+}
+
+
+
+IMPLEMENT_CLASS (MasterServerInterface)
+GET_MANAGER (MasterServerInterface)
+GET_MANAGER_PTR (MasterServerInterface)
+#endif