about summary refs log tree commit diff
path: root/masterserver
diff options
context:
space:
mode:
Diffstat (limited to 'masterserver')
-rw-r--r--masterserver/default.bam18
-rw-r--r--masterserver/include/common.h6
-rw-r--r--masterserver/include/masterserver.h48
-rw-r--r--masterserver/include/network.h13
-rw-r--r--masterserver/include/serverinfo.h69
-rw-r--r--masterserver/src/main.cpp29
-rw-r--r--masterserver/src/masterserver.cpp176
-rw-r--r--masterserver/src/network.cpp39
8 files changed, 398 insertions, 0 deletions
diff --git a/masterserver/default.bam b/masterserver/default.bam
new file mode 100644
index 00000000..06492b70
--- /dev/null
+++ b/masterserver/default.bam
@@ -0,0 +1,18 @@
+baselib = Import("../../baselib/baselib.bam")
+--scheme = Import("scheme/scheme.bam")
+
+settings = NewSettings()
+baselib.use(settings)
+--scheme.use(settings)
+settings.cc.includes:add("include")
+--settings.cc.includes:add("include/python")
+--settings.cc.frameworks:add("Python")
+--//settings.linker.libs:add("/opt/local/lib/python2.5/config/libpython2.5.a")
+--settings.linker.frameworks:add("Python")
+
+src = Collect("src/*.cpp")
+objs = Compile(settings, src)
+exe = Link(settings, "server", objs)
+
+--Target(baselib.lib)
+Target(exe)
diff --git a/masterserver/include/common.h b/masterserver/include/common.h
new file mode 100644
index 00000000..ff03a61b
--- /dev/null
+++ b/masterserver/include/common.h
@@ -0,0 +1,6 @@
+#ifndef _COMMON_H
+#define _COMMON_H
+
+typedef int int32;
+
+#endif
diff --git a/masterserver/include/masterserver.h b/masterserver/include/masterserver.h
new file mode 100644
index 00000000..7e042aaa
--- /dev/null
+++ b/masterserver/include/masterserver.h
@@ -0,0 +1,48 @@
+#ifndef _MASTERSERVER_H
+#define _MASTERSERVER_H
+
+#include <baselib/network.h>
+#include "serverinfo.h"
+
+using namespace baselib;
+
+#define HEARTBEAT_SIZE 216
+#define HEARTBEAT_SIGNATURE 'TWHB'
+#define HEARTBEAT_LIFETIME 10
+#define MAXSERVERS 1024
+#define SERVERINFOOUT_SIZE 212
+#define SERVERINFOHEADER_SIZE 12
+#define MASTERSERVER_VERSION 0
+
+class CMasterServer
+{
+	CServerInfo m_Servers[MAXSERVERS];
+	int m_ServerCount;
+	socket_udp4 m_UDPSocket;
+	socket_tcp4 m_TCPSocket;
+	int m_CurrentTime;
+	char m_ServerListPacket[MAXSERVERS * SERVERINFOOUT_SIZE + SERVERINFOHEADER_SIZE];
+	int m_ServerListPacketSize;
+	bool m_ServerListPacketIsOld;
+
+	void ListenForServerListPolls();
+	void BuildServerListPacket();
+    void ListenForHeartBeats();
+    void ProcessHeartBeat(CServerInfo info);
+    CServerInfo *FindServerInfo(int32 ip, int32 port);
+    CServerInfo *GetUnusedSlot();
+    void CleanUpServerList();
+public:
+	CMasterServer()
+	{
+		m_ServerCount = 0;
+		m_ServerListPacketIsOld = true;
+	}
+
+	void Init(int port);
+	void Shutdown(); 
+		
+	void Tick();
+};
+
+#endif
diff --git a/masterserver/include/network.h b/masterserver/include/network.h
new file mode 100644
index 00000000..455fe681
--- /dev/null
+++ b/masterserver/include/network.h
@@ -0,0 +1,13 @@
+#ifndef _NETWORK_H
+#define _NETWORK_H
+
+#include <cstring>
+#include "common.h"
+
+char *WriteInt32(char *buffer, int32 value);
+char *WriteFixedString(char *buffer, const char *string, int strlen);
+
+char *ReadInt32(char *buffer, int32 *value);
+char *ReadFixedString(char *buffer, char *string, int strlen);
+
+#endif
diff --git a/masterserver/include/serverinfo.h b/masterserver/include/serverinfo.h
new file mode 100644
index 00000000..c63e11ba
--- /dev/null
+++ b/masterserver/include/serverinfo.h
@@ -0,0 +1,69 @@
+#ifndef _SERVERINFO_H
+#define _SERVERINFO_H
+
+#include <baselib/network.h>
+
+#include "common.h"
+#include "network.h"
+
+class CServerInfo
+{
+	int32 m_Version;
+	int32 m_IP;
+	int32 m_Port;
+	int32 m_Players;
+	int32 m_MaxPlayers;
+	char m_Name[128];
+	char m_Map[64];
+
+	int m_LastRefresh;
+
+public:
+	int32 IP() const { return m_IP; }
+	int32 Port() const { return m_Port; }
+	int32 Players() const { return m_Players; }
+	int32 MaxPlayers() const { return m_MaxPlayers; };
+	const char *Name() const { return m_Name; }
+	const char *Map() const { return m_Map; }
+
+	void Refresh(int time) { m_LastRefresh = time; }
+	int LastRefresh() { return m_LastRefresh; }
+
+	void SetAddress(baselib::netaddr4 *addr)
+	{
+		m_IP = addr->ip[0] << 24;
+		m_IP |= addr->ip[1] << 16;
+		m_IP |= addr->ip[2] << 8;
+		m_IP |= addr->ip[3];
+
+		m_Port = addr->port;
+	}
+	
+	char *Serialize(char *buffer) const
+	{
+		buffer = WriteInt32(buffer, m_Version);
+		buffer = WriteInt32(buffer, m_IP);
+		buffer = WriteInt32(buffer, m_Port);
+		buffer = WriteInt32(buffer, m_Players);
+		buffer = WriteInt32(buffer, m_MaxPlayers);
+		buffer = WriteFixedString(buffer, m_Name, sizeof(m_Name));
+		buffer = WriteFixedString(buffer, m_Map, sizeof(m_Map));
+
+		return buffer;
+	}
+
+	char *Deserialize(char *buffer)
+	{
+		buffer = ReadInt32(buffer, &m_Version);
+		buffer = ReadInt32(buffer, &m_IP);
+		buffer = ReadInt32(buffer, &m_Port);
+		buffer = ReadInt32(buffer, &m_Players);
+		buffer = ReadInt32(buffer, &m_MaxPlayers);
+		buffer = ReadFixedString(buffer, m_Name, sizeof(m_Name));
+		buffer = ReadFixedString(buffer, m_Map, sizeof(m_Map));
+
+		return buffer;
+	}
+};
+
+#endif
diff --git a/masterserver/src/main.cpp b/masterserver/src/main.cpp
new file mode 100644
index 00000000..380830ad
--- /dev/null
+++ b/masterserver/src/main.cpp
@@ -0,0 +1,29 @@
+#include <cstdio>
+#include <cstdlib>
+
+#include "masterserver.h"
+
+int main(int argc, char *argv[])
+{
+	if (argc != 2)
+	{
+		puts("Usage: masterserver <port>    (this will bind the server to the port specified (both udp and tcp).");
+		return -1;
+	}
+
+	int port = atoi(argv[1]);
+
+	CMasterServer masterServer;
+	masterServer.Init(port);
+
+	while (1)
+	{
+		masterServer.Tick();
+
+		thread_sleep(10);
+	}
+
+	masterServer.Shutdown();
+	
+	return 0;
+}
diff --git a/masterserver/src/masterserver.cpp b/masterserver/src/masterserver.cpp
new file mode 100644
index 00000000..383c696e
--- /dev/null
+++ b/masterserver/src/masterserver.cpp
@@ -0,0 +1,176 @@
+#include <ctime>
+
+#include "masterserver.h"
+
+void CMasterServer::Init(int port)
+{
+	netaddr4 addr(0, 0, 0, 0, port);
+	addr.port = port;
+	
+	net_init();
+	m_UDPSocket.open(port);
+	m_TCPSocket.open(&addr);
+	m_TCPSocket.set_non_blocking();
+	m_TCPSocket.listen();
+}
+
+void CMasterServer::Shutdown()
+{
+	m_UDPSocket.close();
+}
+
+void CMasterServer::Tick()
+{
+	m_CurrentTime = time(NULL);
+	
+	ListenForHeartBeats();
+	ListenForServerListPolls();
+
+	CleanUpServerList();
+}
+
+void CMasterServer::ListenForHeartBeats()
+{
+	netaddr4 from;
+	char data[1024];
+	int dataSize;
+
+	// read udp data while there is data to read :)
+	while ((dataSize = m_UDPSocket.recv(&from, (char *)data, sizeof(data))) > 0)
+	{
+		// compare the received data size to the expected size
+		if (dataSize == HEARTBEAT_SIZE)
+		{
+			char *d = data;
+			int32 signature;
+			d = ReadInt32(d, &signature);
+		
+			// make sure the signature is correct
+			if (signature == HEARTBEAT_SIGNATURE)
+			{
+				CServerInfo info;
+				info.Deserialize(d);
+
+				from.port = 8303;
+				info.SetAddress(&from);
+
+				dbg_msg("got heartbeat", "IP: %i.%i.%i.%i:%i", (int)from.ip[0], (int)from.ip[1], (int)from.ip[2], (int)from.ip[3], from.port);
+
+				dbg_msg("refresh", "okay. server count: %i", m_ServerCount);
+
+				ProcessHeartBeat(info);	
+			}
+			else
+			{}	// unexpected signature
+		}
+		else
+		{}	// unknown data received
+	}
+}
+
+void CMasterServer::ProcessHeartBeat(CServerInfo info)
+{
+	// find the corresponding server info
+	CServerInfo *serverInfo = FindServerInfo(info.IP(), info.Port());
+
+	// if it isn't in the list already, try to get an unused slot
+	if (!serverInfo)
+		serverInfo = GetUnusedSlot();
+
+	// if we STILL don't have one, we're out of luck.
+	if (!serverInfo)
+		return;
+	
+	*serverInfo = info;
+	serverInfo->Refresh(m_CurrentTime);
+
+	// mark the server list packet as old
+	m_ServerListPacketIsOld = true;
+}
+
+CServerInfo *CMasterServer::FindServerInfo(int32 ip, int32 port)
+{
+	// for now, just loop over the array
+	for (int i = 0; i < m_ServerCount; i++)
+	{
+		CServerInfo *info = &m_Servers[i];
+		if (info->IP() == ip && info->Port() == port)
+			return info;
+	}
+
+	return 0x0;
+}
+
+CServerInfo *CMasterServer::GetUnusedSlot()
+{
+	if (m_ServerCount == MAXSERVERS)
+		return 0x0;
+	else
+		return &m_Servers[m_ServerCount++];
+}
+
+void CMasterServer::CleanUpServerList()
+{
+	for (int i = 0; i < m_ServerCount; i++)
+	{
+		CServerInfo *serverInfo = &m_Servers[i];
+		
+		// check if it's time to remove it from the list
+		if (serverInfo->LastRefresh() + HEARTBEAT_LIFETIME < m_CurrentTime)
+		{
+			if (i + 1 == m_ServerCount)
+			{
+				// if we're at the last one, just decrease m_ServerCount
+				--m_ServerCount;
+			}
+			else
+			{
+				// otherwise, copy the last slot here and then decrease i and m_ServerCount
+				*serverInfo = m_Servers[m_ServerCount - 1];
+				--i;
+				--m_ServerCount;
+			}
+
+			// mark the server list packet as old and outdated
+			m_ServerListPacketIsOld = true;
+		}
+	}
+}
+
+void CMasterServer::ListenForServerListPolls()
+{
+	socket_tcp4 client;
+
+	// accept clients while there are clients to be accepted
+	while (m_TCPSocket.accept(&client))
+	{
+		// maybe we've prepared the packet already... it'd be silly to do it twice
+		if (m_ServerListPacketIsOld)
+		{
+			BuildServerListPacket();
+		}
+		
+		// send the server list and then close the socket
+		client.send(m_ServerListPacket, m_ServerListPacketSize);
+		client.close();
+	}
+}
+
+void CMasterServer::BuildServerListPacket()
+{
+	char *d = m_ServerListPacket;
+
+	d = WriteInt32(d, 'TWSL');
+	d = WriteInt32(d, MASTERSERVER_VERSION);
+
+	d = WriteInt32(d, m_ServerCount);
+	
+	for (int i = 0; i < m_ServerCount; i++)
+	{
+		CServerInfo *info = &m_Servers[i];
+		d = info->Serialize(d);
+	}
+
+	m_ServerListPacketSize = d - m_ServerListPacket;
+	m_ServerListPacketIsOld = false;
+}
diff --git a/masterserver/src/network.cpp b/masterserver/src/network.cpp
new file mode 100644
index 00000000..7d557cdf
--- /dev/null
+++ b/masterserver/src/network.cpp
@@ -0,0 +1,39 @@
+#include <cstring>
+#include "common.h"
+#include "network.h"
+
+char *WriteInt32(char *buffer, int32 value)
+{
+	buffer[0] = value >> 24;
+	buffer[1] = value >> 16;
+	buffer[2] = value >> 8;
+	buffer[3] = value;
+
+	return buffer + sizeof(int32);
+}
+
+char *WriteFixedString(char *buffer, const char *string, int strlen)
+{
+	memcpy(buffer, string, strlen);
+
+	return buffer + strlen;
+}
+
+
+
+char *ReadInt32(char *buffer, int32 *value)
+{
+	*value = buffer[0] << 24;
+	*value |= buffer[1] << 16;
+	*value |= buffer[2] << 8;
+	*value |= buffer[3];
+
+	return buffer + sizeof(int32);
+}
+
+char *ReadFixedString(char *buffer, char *string, int strlen)
+{
+	memcpy(string, buffer, strlen);
+
+	return buffer + strlen;
+}