summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO24
-rw-r--r--0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch835
-rw-r--r--PKGBUILD48
-rw-r--r--README57
-rw-r--r--server.sh.in12
5 files changed, 976 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 00000000000..84230eafb11
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,24 @@
+pkgbase = teeworlds-hunter
+ pkgdesc = A customized server by bluelightzero where a Tee hunts everyone else
+ pkgver = 0.6.2.r6.944705e
+ pkgrel = 1
+ url = https://www.teeworlds.com/forum/viewtopic.php?id=10408
+ arch = i686
+ arch = x86_64
+ license = custom
+ makedepends = python
+ makedepends = bam
+ makedepends = mesa
+ makedepends = unzip
+ depends = gcc-libs
+ source = git+https://github.com/bluelightzero/teeworlds-hunter.git
+ source = 0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch
+ source = server.sh.in
+ source = README
+ md5sums = SKIP
+ md5sums = a76fcf155a7a38a51d95ad1ff0050472
+ md5sums = 529146a3b13993770fa414f66c67fa26
+ md5sums = 81785338a2084be394a997a465e33027
+
+pkgname = teeworlds-hunter
+
diff --git a/0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch b/0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch
new file mode 100644
index 00000000000..36f0f7dea13
--- /dev/null
+++ b/0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch
@@ -0,0 +1,835 @@
+From b560395a680471426dc2e93a68eb3be79bb1b8f9 Mon Sep 17 00:00:00 2001
+From: Rafael Ferreira <rafael.f.f1@gmail.com>
+Date: Fri, 22 Aug 2014 06:14:03 -0300
+Subject: [PATCH] Enabled mastersrv and versionsrv as they are required
+
+---
+ .gitignore | 3 +-
+ src/mastersrv/mastersrv.cpp | 542 ++++++++++++++++++++++++++++++++++++++++++
+ src/mastersrv/mastersrv.h | 52 ++++
+ src/versionsrv/mapversions.h | 22 ++
+ src/versionsrv/versionsrv.cpp | 133 +++++++++++
+ src/versionsrv/versionsrv.h | 19 ++
+ 6 files changed, 769 insertions(+), 2 deletions(-)
+ create mode 100644 src/mastersrv/mastersrv.cpp
+ create mode 100644 src/mastersrv/mastersrv.h
+ create mode 100644 src/versionsrv/mapversions.h
+ create mode 100644 src/versionsrv/versionsrv.cpp
+ create mode 100644 src/versionsrv/versionsrv.h
+
+diff --git a/.gitignore b/.gitignore
+index 4b1a4ad..93efac2 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -16,9 +16,8 @@ dilate*
+ fake_server*
+ map_resave*
+ map_version*
+-mastersrv*
+ packetgen*
+ teeworlds*
+ teeworlds_srv*
+ tileset_border*
+-versionsrv*
++
+diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp
+new file mode 100644
+index 0000000..954650a
+--- /dev/null
++++ b/src/mastersrv/mastersrv.cpp
+@@ -0,0 +1,542 @@
++/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
++/* If you are missing that file, acquire a complete release at teeworlds.com. */
++#include <base/system.h>
++
++#include <engine/config.h>
++#include <engine/console.h>
++#include <engine/kernel.h>
++#include <engine/storage.h>
++
++#include <engine/shared/config.h>
++#include <engine/shared/netban.h>
++#include <engine/shared/network.h>
++
++#include "mastersrv.h"
++
++
++enum {
++ MTU = 1400,
++ MAX_SERVERS_PER_PACKET=75,
++ MAX_PACKETS=16,
++ MAX_SERVERS=MAX_SERVERS_PER_PACKET*MAX_PACKETS,
++ EXPIRE_TIME = 90
++};
++
++struct CCheckServer
++{
++ enum ServerType m_Type;
++ NETADDR m_Address;
++ NETADDR m_AltAddress;
++ int m_TryCount;
++ int64 m_TryTime;
++};
++
++static CCheckServer m_aCheckServers[MAX_SERVERS];
++static int m_NumCheckServers = 0;
++
++struct CServerEntry
++{
++ enum ServerType m_Type;
++ NETADDR m_Address;
++ int64 m_Expire;
++};
++
++static CServerEntry m_aServers[MAX_SERVERS];
++static int m_NumServers = 0;
++
++struct CPacketData
++{
++ int m_Size;
++ struct {
++ unsigned char m_aHeader[sizeof(SERVERBROWSE_LIST)];
++ CMastersrvAddr m_aServers[MAX_SERVERS_PER_PACKET];
++ } m_Data;
++};
++
++CPacketData m_aPackets[MAX_PACKETS];
++static int m_NumPackets = 0;
++
++// legacy code
++struct CPacketDataLegacy
++{
++ int m_Size;
++ struct {
++ unsigned char m_aHeader[sizeof(SERVERBROWSE_LIST_LEGACY)];
++ CMastersrvAddrLegacy m_aServers[MAX_SERVERS_PER_PACKET];
++ } m_Data;
++};
++
++CPacketDataLegacy m_aPacketsLegacy[MAX_PACKETS];
++static int m_NumPacketsLegacy = 0;
++
++
++struct CCountPacketData
++{
++ unsigned char m_Header[sizeof(SERVERBROWSE_COUNT)];
++ unsigned char m_High;
++ unsigned char m_Low;
++};
++
++static CCountPacketData m_CountData;
++static CCountPacketData m_CountDataLegacy;
++
++
++CNetBan m_NetBan;
++
++static CNetClient m_NetChecker; // NAT/FW checker
++static CNetClient m_NetOp; // main
++
++IConsole *m_pConsole;
++
++void BuildPackets()
++{
++ CServerEntry *pCurrent = &m_aServers[0];
++ int ServersLeft = m_NumServers;
++ m_NumPackets = 0;
++ m_NumPacketsLegacy = 0;
++ int PacketIndex = 0;
++ int PacketIndexLegacy = 0;
++ while(ServersLeft-- && (m_NumPackets + m_NumPacketsLegacy) < MAX_PACKETS)
++ {
++ if(pCurrent->m_Type == SERVERTYPE_NORMAL)
++ {
++ if(PacketIndex % MAX_SERVERS_PER_PACKET == 0)
++ {
++ PacketIndex = 0;
++ m_NumPackets++;
++ }
++
++ // copy header
++ mem_copy(m_aPackets[m_NumPackets-1].m_Data.m_aHeader, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST));
++
++ // copy server addresses
++ if(pCurrent->m_Address.type == NETTYPE_IPV6)
++ {
++ mem_copy(m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp, pCurrent->m_Address.ip,
++ sizeof(m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp));
++ }
++ else
++ {
++ static unsigned char s_aIPV4Mapping[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF};
++
++ mem_copy(m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp, s_aIPV4Mapping, sizeof(s_aIPV4Mapping));
++ m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp[12] = pCurrent->m_Address.ip[0];
++ m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp[13] = pCurrent->m_Address.ip[1];
++ m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp[14] = pCurrent->m_Address.ip[2];
++ m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aIp[15] = pCurrent->m_Address.ip[3];
++ }
++
++ m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aPort[0] = (pCurrent->m_Address.port>>8)&0xff;
++ m_aPackets[m_NumPackets-1].m_Data.m_aServers[PacketIndex].m_aPort[1] = pCurrent->m_Address.port&0xff;
++
++ PacketIndex++;
++
++ m_aPackets[m_NumPackets-1].m_Size = sizeof(SERVERBROWSE_LIST) + sizeof(CMastersrvAddr)*PacketIndex;
++
++ pCurrent++;
++ }
++ else if(pCurrent->m_Type == SERVERTYPE_LEGACY)
++ {
++ if(PacketIndexLegacy % MAX_SERVERS_PER_PACKET == 0)
++ {
++ PacketIndexLegacy = 0;
++ m_NumPacketsLegacy++;
++ }
++
++ // copy header
++ mem_copy(m_aPacketsLegacy[m_NumPacketsLegacy-1].m_Data.m_aHeader, SERVERBROWSE_LIST_LEGACY, sizeof(SERVERBROWSE_LIST_LEGACY));
++
++ // copy server addresses
++ mem_copy(m_aPacketsLegacy[m_NumPacketsLegacy-1].m_Data.m_aServers[PacketIndexLegacy].m_aIp, pCurrent->m_Address.ip,
++ sizeof(m_aPacketsLegacy[m_NumPacketsLegacy-1].m_Data.m_aServers[PacketIndexLegacy].m_aIp));
++ // 0.5 has the port in little endian on the network
++ m_aPacketsLegacy[m_NumPacketsLegacy-1].m_Data.m_aServers[PacketIndexLegacy].m_aPort[0] = pCurrent->m_Address.port&0xff;
++ m_aPacketsLegacy[m_NumPacketsLegacy-1].m_Data.m_aServers[PacketIndexLegacy].m_aPort[1] = (pCurrent->m_Address.port>>8)&0xff;
++
++ PacketIndexLegacy++;
++
++ m_aPacketsLegacy[m_NumPacketsLegacy-1].m_Size = sizeof(SERVERBROWSE_LIST_LEGACY) + sizeof(CMastersrvAddrLegacy)*PacketIndexLegacy;
++
++ pCurrent++;
++ }
++ else
++ {
++ *pCurrent = m_aServers[m_NumServers-1];
++ m_NumServers--;
++ dbg_msg("mastersrv", "error: server of invalid type, dropping it");
++ }
++ }
++}
++
++void SendOk(NETADDR *pAddr)
++{
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = *pAddr;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++ p.m_DataSize = sizeof(SERVERBROWSE_FWOK);
++ p.m_pData = SERVERBROWSE_FWOK;
++
++ // send on both to be sure
++ m_NetChecker.Send(&p);
++ m_NetOp.Send(&p);
++}
++
++void SendError(NETADDR *pAddr)
++{
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = *pAddr;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++ p.m_DataSize = sizeof(SERVERBROWSE_FWERROR);
++ p.m_pData = SERVERBROWSE_FWERROR;
++ m_NetOp.Send(&p);
++}
++
++void SendCheck(NETADDR *pAddr)
++{
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = *pAddr;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++ p.m_DataSize = sizeof(SERVERBROWSE_FWCHECK);
++ p.m_pData = SERVERBROWSE_FWCHECK;
++ m_NetChecker.Send(&p);
++}
++
++void AddCheckserver(NETADDR *pInfo, NETADDR *pAlt, ServerType Type)
++{
++ // add server
++ if(m_NumCheckServers == MAX_SERVERS)
++ {
++ dbg_msg("mastersrv", "error: mastersrv is full");
++ return;
++ }
++
++ char aAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(pInfo, aAddrStr, sizeof(aAddrStr), true);
++ char aAltAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(pAlt, aAltAddrStr, sizeof(aAltAddrStr), true);
++ dbg_msg("mastersrv", "checking: %s (%s)", aAddrStr, aAltAddrStr);
++ m_aCheckServers[m_NumCheckServers].m_Address = *pInfo;
++ m_aCheckServers[m_NumCheckServers].m_AltAddress = *pAlt;
++ m_aCheckServers[m_NumCheckServers].m_TryCount = 0;
++ m_aCheckServers[m_NumCheckServers].m_TryTime = 0;
++ m_aCheckServers[m_NumCheckServers].m_Type = Type;
++ m_NumCheckServers++;
++}
++
++void AddServer(NETADDR *pInfo, ServerType Type)
++{
++ // see if server already exists in list
++ for(int i = 0; i < m_NumServers; i++)
++ {
++ if(net_addr_comp(&m_aServers[i].m_Address, pInfo) == 0)
++ {
++ char aAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(pInfo, aAddrStr, sizeof(aAddrStr), true);
++ dbg_msg("mastersrv", "updated: %s", aAddrStr);
++ m_aServers[i].m_Expire = time_get()+time_freq()*EXPIRE_TIME;
++ return;
++ }
++ }
++
++ // add server
++ if(m_NumServers == MAX_SERVERS)
++ {
++ dbg_msg("mastersrv", "error: mastersrv is full");
++ return;
++ }
++
++ char aAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(pInfo, aAddrStr, sizeof(aAddrStr), true);
++ dbg_msg("mastersrv", "added: %s", aAddrStr);
++ m_aServers[m_NumServers].m_Address = *pInfo;
++ m_aServers[m_NumServers].m_Expire = time_get()+time_freq()*EXPIRE_TIME;
++ m_aServers[m_NumServers].m_Type = Type;
++ m_NumServers++;
++}
++
++void UpdateServers()
++{
++ int64 Now = time_get();
++ int64 Freq = time_freq();
++ for(int i = 0; i < m_NumCheckServers; i++)
++ {
++ if(Now > m_aCheckServers[i].m_TryTime+Freq)
++ {
++ if(m_aCheckServers[i].m_TryCount == 10)
++ {
++ char aAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(&m_aCheckServers[i].m_Address, aAddrStr, sizeof(aAddrStr), true);
++ char aAltAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(&m_aCheckServers[i].m_AltAddress, aAltAddrStr, sizeof(aAltAddrStr), true);
++ dbg_msg("mastersrv", "check failed: %s (%s)", aAddrStr, aAltAddrStr);
++
++ // FAIL!!
++ SendError(&m_aCheckServers[i].m_Address);
++ m_aCheckServers[i] = m_aCheckServers[m_NumCheckServers-1];
++ m_NumCheckServers--;
++ i--;
++ }
++ else
++ {
++ m_aCheckServers[i].m_TryCount++;
++ m_aCheckServers[i].m_TryTime = Now;
++ if(m_aCheckServers[i].m_TryCount&1)
++ SendCheck(&m_aCheckServers[i].m_Address);
++ else
++ SendCheck(&m_aCheckServers[i].m_AltAddress);
++ }
++ }
++ }
++}
++
++void PurgeServers()
++{
++ int64 Now = time_get();
++ int i = 0;
++ while(i < m_NumServers)
++ {
++ if(m_aServers[i].m_Expire < Now)
++ {
++ // remove server
++ char aAddrStr[NETADDR_MAXSTRSIZE];
++ net_addr_str(&m_aServers[i].m_Address, aAddrStr, sizeof(aAddrStr), true);
++ dbg_msg("mastersrv", "expired: %s", aAddrStr);
++ m_aServers[i] = m_aServers[m_NumServers-1];
++ m_NumServers--;
++ }
++ else
++ i++;
++ }
++}
++
++void ReloadBans()
++{
++ m_NetBan.UnbanAll();
++ m_pConsole->ExecuteFile("master.cfg");
++}
++
++int main(int argc, const char **argv) // ignore_convention
++{
++ int64 LastBuild = 0, LastBanReload = 0;
++ ServerType Type = SERVERTYPE_INVALID;
++ NETADDR BindAddr;
++
++ dbg_logger_stdout();
++ net_init();
++
++ mem_copy(m_CountData.m_Header, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT));
++ mem_copy(m_CountDataLegacy.m_Header, SERVERBROWSE_COUNT_LEGACY, sizeof(SERVERBROWSE_COUNT_LEGACY));
++
++ IKernel *pKernel = IKernel::Create();
++ IStorage *pStorage = CreateStorage("Teeworlds", IStorage::STORAGETYPE_BASIC, argc, argv);
++ IConfig *pConfig = CreateConfig();
++ m_pConsole = CreateConsole(CFGFLAG_MASTER);
++
++ bool RegisterFail = !pKernel->RegisterInterface(pStorage);
++ RegisterFail |= !pKernel->RegisterInterface(m_pConsole);
++ RegisterFail |= !pKernel->RegisterInterface(pConfig);
++
++ if(RegisterFail)
++ return -1;
++
++ pConfig->Init();
++ m_NetBan.Init(m_pConsole, pStorage);
++ if(argc > 1) // ignore_convention
++ m_pConsole->ParseArguments(argc-1, &argv[1]); // ignore_convention
++
++ if(g_Config.m_Bindaddr[0] && net_host_lookup(g_Config.m_Bindaddr, &BindAddr, NETTYPE_ALL) == 0)
++ {
++ // got bindaddr
++ BindAddr.type = NETTYPE_ALL;
++ BindAddr.port = MASTERSERVER_PORT;
++ }
++ else
++ {
++ mem_zero(&BindAddr, sizeof(BindAddr));
++ BindAddr.type = NETTYPE_ALL;
++ BindAddr.port = MASTERSERVER_PORT;
++ }
++
++ if(!m_NetOp.Open(BindAddr, 0))
++ {
++ dbg_msg("mastersrv", "couldn't start network (op)");
++ return -1;
++ }
++ BindAddr.port = MASTERSERVER_PORT+1;
++ if(!m_NetChecker.Open(BindAddr, 0))
++ {
++ dbg_msg("mastersrv", "couldn't start network (checker)");
++ return -1;
++ }
++
++ // process pending commands
++ m_pConsole->StoreCommands(false);
++
++ dbg_msg("mastersrv", "started");
++
++ while(1)
++ {
++ m_NetOp.Update();
++ m_NetChecker.Update();
++
++ // process m_aPackets
++ CNetChunk Packet;
++ while(m_NetOp.Recv(&Packet))
++ {
++ // check if the server is banned
++ if(m_NetBan.IsBanned(&Packet.m_Address, 0, 0))
++ continue;
++
++ if(Packet.m_DataSize == sizeof(SERVERBROWSE_HEARTBEAT)+2 &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0)
++ {
++ NETADDR Alt;
++ unsigned char *d = (unsigned char *)Packet.m_pData;
++ Alt = Packet.m_Address;
++ Alt.port =
++ (d[sizeof(SERVERBROWSE_HEARTBEAT)]<<8) |
++ d[sizeof(SERVERBROWSE_HEARTBEAT)+1];
++
++ // add it
++ AddCheckserver(&Packet.m_Address, &Alt, SERVERTYPE_NORMAL);
++ }
++ else if(Packet.m_DataSize == sizeof(SERVERBROWSE_HEARTBEAT_LEGACY)+2 &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_HEARTBEAT_LEGACY, sizeof(SERVERBROWSE_HEARTBEAT_LEGACY)) == 0)
++ {
++ NETADDR Alt;
++ unsigned char *d = (unsigned char *)Packet.m_pData;
++ Alt = Packet.m_Address;
++ Alt.port =
++ (d[sizeof(SERVERBROWSE_HEARTBEAT)]<<8) |
++ d[sizeof(SERVERBROWSE_HEARTBEAT)+1];
++
++ // add it
++ AddCheckserver(&Packet.m_Address, &Alt, SERVERTYPE_LEGACY);
++ }
++
++ else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETCOUNT) &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_GETCOUNT, sizeof(SERVERBROWSE_GETCOUNT)) == 0)
++ {
++ dbg_msg("mastersrv", "count requested, responding with %d", m_NumServers);
++
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = Packet.m_Address;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++ p.m_DataSize = sizeof(m_CountData);
++ p.m_pData = &m_CountData;
++ m_CountData.m_High = (m_NumServers>>8)&0xff;
++ m_CountData.m_Low = m_NumServers&0xff;
++ m_NetOp.Send(&p);
++ }
++ else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETCOUNT_LEGACY) &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_GETCOUNT_LEGACY, sizeof(SERVERBROWSE_GETCOUNT_LEGACY)) == 0)
++ {
++ dbg_msg("mastersrv", "count requested, responding with %d", m_NumServers);
++
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = Packet.m_Address;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++ p.m_DataSize = sizeof(m_CountData);
++ p.m_pData = &m_CountDataLegacy;
++ m_CountDataLegacy.m_High = (m_NumServers>>8)&0xff;
++ m_CountDataLegacy.m_Low = m_NumServers&0xff;
++ m_NetOp.Send(&p);
++ }
++ else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETLIST) &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0)
++ {
++ // someone requested the list
++ dbg_msg("mastersrv", "requested, responding with %d m_aServers", m_NumServers);
++
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = Packet.m_Address;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++
++ for(int i = 0; i < m_NumPackets; i++)
++ {
++ p.m_DataSize = m_aPackets[i].m_Size;
++ p.m_pData = &m_aPackets[i].m_Data;
++ m_NetOp.Send(&p);
++ }
++ }
++ else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETLIST_LEGACY) &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_GETLIST_LEGACY, sizeof(SERVERBROWSE_GETLIST_LEGACY)) == 0)
++ {
++ // someone requested the list
++ dbg_msg("mastersrv", "requested, responding with %d m_aServers", m_NumServers);
++
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = Packet.m_Address;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++
++ for(int i = 0; i < m_NumPacketsLegacy; i++)
++ {
++ p.m_DataSize = m_aPacketsLegacy[i].m_Size;
++ p.m_pData = &m_aPacketsLegacy[i].m_Data;
++ m_NetOp.Send(&p);
++ }
++ }
++ }
++
++ // process m_aPackets
++ while(m_NetChecker.Recv(&Packet))
++ {
++ // check if the server is banned
++ if(m_NetBan.IsBanned(&Packet.m_Address, 0, 0))
++ continue;
++
++ if(Packet.m_DataSize == sizeof(SERVERBROWSE_FWRESPONSE) &&
++ mem_comp(Packet.m_pData, SERVERBROWSE_FWRESPONSE, sizeof(SERVERBROWSE_FWRESPONSE)) == 0)
++ {
++ Type = SERVERTYPE_INVALID;
++ // remove it from checking
++ for(int i = 0; i < m_NumCheckServers; i++)
++ {
++ if(net_addr_comp(&m_aCheckServers[i].m_Address, &Packet.m_Address) == 0 ||
++ net_addr_comp(&m_aCheckServers[i].m_AltAddress, &Packet.m_Address) == 0)
++ {
++ Type = m_aCheckServers[i].m_Type;
++ m_NumCheckServers--;
++ m_aCheckServers[i] = m_aCheckServers[m_NumCheckServers];
++ break;
++ }
++ }
++
++ // drops servers that were not in the CheckServers list
++ if(Type == SERVERTYPE_INVALID)
++ continue;
++
++ AddServer(&Packet.m_Address, Type);
++ SendOk(&Packet.m_Address);
++ }
++ }
++
++ if(time_get()-LastBanReload > time_freq()*300)
++ {
++ LastBanReload = time_get();
++
++ ReloadBans();
++ }
++
++ if(time_get()-LastBuild > time_freq()*5)
++ {
++ LastBuild = time_get();
++
++ PurgeServers();
++ UpdateServers();
++ BuildPackets();
++ }
++
++ // be nice to the CPU
++ thread_sleep(1);
++ }
++
++ return 0;
++}
+diff --git a/src/mastersrv/mastersrv.h b/src/mastersrv/mastersrv.h
+new file mode 100644
+index 0000000..38e5ab2
+--- /dev/null
++++ b/src/mastersrv/mastersrv.h
+@@ -0,0 +1,52 @@
++/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
++/* If you are missing that file, acquire a complete release at teeworlds.com. */
++#ifndef MASTERSRV_MASTERSRV_H
++#define MASTERSRV_MASTERSRV_H
++static const int MASTERSERVER_PORT = 8300;
++
++enum ServerType
++{
++ SERVERTYPE_INVALID = -1,
++ SERVERTYPE_NORMAL,
++ SERVERTYPE_LEGACY
++};
++
++struct CMastersrvAddr
++{
++ unsigned char m_aIp[16];
++ unsigned char m_aPort[2];
++};
++
++static const unsigned char SERVERBROWSE_HEARTBEAT[] = {255, 255, 255, 255, 'b', 'e', 'a', '2'};
++
++static const unsigned char SERVERBROWSE_GETLIST[] = {255, 255, 255, 255, 'r', 'e', 'q', '2'};
++static const unsigned char SERVERBROWSE_LIST[] = {255, 255, 255, 255, 'l', 'i', 's', '2'};
++
++static const unsigned char SERVERBROWSE_GETCOUNT[] = {255, 255, 255, 255, 'c', 'o', 'u', '2'};
++static const unsigned char SERVERBROWSE_COUNT[] = {255, 255, 255, 255, 's', 'i', 'z', '2'};
++
++static const unsigned char SERVERBROWSE_GETINFO[] = {255, 255, 255, 255, 'g', 'i', 'e', '3'};
++static const unsigned char SERVERBROWSE_INFO[] = {255, 255, 255, 255, 'i', 'n', 'f', '3'};
++
++static const unsigned char SERVERBROWSE_FWCHECK[] = {255, 255, 255, 255, 'f', 'w', '?', '?'};
++static const unsigned char SERVERBROWSE_FWRESPONSE[] = {255, 255, 255, 255, 'f', 'w', '!', '!'};
++static const unsigned char SERVERBROWSE_FWOK[] = {255, 255, 255, 255, 'f', 'w', 'o', 'k'};
++static const unsigned char SERVERBROWSE_FWERROR[] = {255, 255, 255, 255, 'f', 'w', 'e', 'r'};
++
++
++// packet headers for the 0.5 branch
++
++struct CMastersrvAddrLegacy
++{
++ unsigned char m_aIp[4];
++ unsigned char m_aPort[2];
++};
++
++static const unsigned char SERVERBROWSE_HEARTBEAT_LEGACY[] = {255, 255, 255, 255, 'b', 'e', 'a', 't'};
++
++static const unsigned char SERVERBROWSE_GETLIST_LEGACY[] = {255, 255, 255, 255, 'r', 'e', 'q', 't'};
++static const unsigned char SERVERBROWSE_LIST_LEGACY[] = {255, 255, 255, 255, 'l', 'i', 's', 't'};
++
++static const unsigned char SERVERBROWSE_GETCOUNT_LEGACY[] = {255, 255, 255, 255, 'c', 'o', 'u', 'n'};
++static const unsigned char SERVERBROWSE_COUNT_LEGACY[] = {255, 255, 255, 255, 's', 'i', 'z', 'e'};
++#endif
+diff --git a/src/versionsrv/mapversions.h b/src/versionsrv/mapversions.h
+new file mode 100644
+index 0000000..1d62681
+--- /dev/null
++++ b/src/versionsrv/mapversions.h
+@@ -0,0 +1,22 @@
++/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
++/* If you are missing that file, acquire a complete release at teeworlds.com. */
++#ifndef VERSIONSRV_MAPVERSIONS_H
++#define VERSIONSRV_MAPVERSIONS_H
++
++static CMapVersion s_aMapVersionList[] = {
++ {"ctf1", {0x06, 0xb5, 0xf1, 0x17}, {0x00, 0x00, 0x12, 0x38}},
++ {"ctf2", {0x27, 0xbc, 0x5e, 0xac}, {0x00, 0x00, 0x64, 0x1a}},
++ {"ctf3", {0xa3, 0x73, 0x9d, 0x41}, {0x00, 0x00, 0x17, 0x0f}},
++ {"ctf4", {0xbe, 0x7c, 0x4d, 0xb9}, {0x00, 0x00, 0x2e, 0xfe}},
++ {"ctf5", {0xd9, 0x21, 0x29, 0xa0}, {0x00, 0x00, 0x2f, 0x4c}},
++ {"ctf6", {0x28, 0xc8, 0x43, 0x51}, {0x00, 0x00, 0x69, 0x2f}},
++ {"ctf7", {0x1d, 0x35, 0x98, 0x72}, {0x00, 0x00, 0x15, 0x87}},
++ {"dm1", {0xf2, 0x15, 0x9e, 0x6e}, {0x00, 0x00, 0x16, 0xad}},
++ {"dm2", {0x71, 0x83, 0x98, 0x78}, {0x00, 0x00, 0x21, 0xdf}},
++ {"dm6", {0x47, 0x4d, 0xa2, 0x35}, {0x00, 0x00, 0x1e, 0x95}},
++ {"dm7", {0x42, 0x6d, 0xa1, 0x67}, {0x00, 0x00, 0x27, 0x2a}},
++ {"dm8", {0x2f, 0x74, 0xd9, 0x18}, {0x00, 0x00, 0xa1, 0xa1}},
++ {"dm9", {0x42, 0xd4, 0x77, 0x7e}, {0x00, 0x00, 0x20, 0x11}},
++};
++static const int s_NumMapVersionItems = sizeof(s_aMapVersionList)/sizeof(CMapVersion);
++#endif
+diff --git a/src/versionsrv/versionsrv.cpp b/src/versionsrv/versionsrv.cpp
+new file mode 100644
+index 0000000..9d6e296
+--- /dev/null
++++ b/src/versionsrv/versionsrv.cpp
+@@ -0,0 +1,133 @@
++/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
++/* If you are missing that file, acquire a complete release at teeworlds.com. */
++#include <base/system.h>
++
++#include <engine/shared/network.h>
++
++#include <game/version.h>
++
++#include "versionsrv.h"
++#include "mapversions.h"
++
++enum {
++ MAX_MAPS_PER_PACKET=48,
++ MAX_PACKETS=16,
++ MAX_MAPS=MAX_MAPS_PER_PACKET*MAX_PACKETS,
++};
++
++struct CPacketData
++{
++ int m_Size;
++ struct {
++ unsigned char m_aHeader[sizeof(VERSIONSRV_MAPLIST)];
++ CMapVersion m_aMaplist[MAX_MAPS_PER_PACKET];
++ } m_Data;
++};
++
++CPacketData m_aPackets[MAX_PACKETS];
++static int m_NumPackets = 0;
++
++static CNetClient g_NetOp; // main
++
++void BuildPackets()
++{
++ CMapVersion *pCurrent = &s_aMapVersionList[0];
++ int ServersLeft = s_NumMapVersionItems;
++ m_NumPackets = 0;
++ while(ServersLeft && m_NumPackets < MAX_PACKETS)
++ {
++ int Chunk = ServersLeft;
++ if(Chunk > MAX_MAPS_PER_PACKET)
++ Chunk = MAX_MAPS_PER_PACKET;
++ ServersLeft -= Chunk;
++
++ // copy header
++ mem_copy(m_aPackets[m_NumPackets].m_Data.m_aHeader, VERSIONSRV_MAPLIST, sizeof(VERSIONSRV_MAPLIST));
++
++ // copy map versions
++ for(int i = 0; i < Chunk; i++)
++ {
++ m_aPackets[m_NumPackets].m_Data.m_aMaplist[i] = *pCurrent;
++ pCurrent++;
++ }
++
++ m_aPackets[m_NumPackets].m_Size = sizeof(VERSIONSRV_MAPLIST) + sizeof(CMapVersion)*Chunk;
++
++ m_NumPackets++;
++ }
++}
++
++void SendVer(NETADDR *pAddr)
++{
++ CNetChunk p;
++ unsigned char aData[sizeof(VERSIONSRV_VERSION) + sizeof(GAME_RELEASE_VERSION)];
++
++ mem_copy(aData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION));
++ mem_copy(aData + sizeof(VERSIONSRV_VERSION), GAME_RELEASE_VERSION, sizeof(GAME_RELEASE_VERSION));
++
++ p.m_ClientID = -1;
++ p.m_Address = *pAddr;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++ p.m_pData = aData;
++ p.m_DataSize = sizeof(aData);
++
++ g_NetOp.Send(&p);
++}
++
++int main(int argc, char **argv) // ignore_convention
++{
++ NETADDR BindAddr;
++
++ dbg_logger_stdout();
++ net_init();
++
++ mem_zero(&BindAddr, sizeof(BindAddr));
++ BindAddr.type = NETTYPE_ALL;
++ BindAddr.port = VERSIONSRV_PORT;
++ if(!g_NetOp.Open(BindAddr, 0))
++ {
++ dbg_msg("mastersrv", "couldn't start network");
++ return -1;
++ }
++
++ BuildPackets();
++
++ dbg_msg("versionsrv", "started");
++
++ while(1)
++ {
++ g_NetOp.Update();
++
++ // process packets
++ CNetChunk Packet;
++ while(g_NetOp.Recv(&Packet))
++ {
++ if(Packet.m_DataSize == sizeof(VERSIONSRV_GETVERSION) &&
++ mem_comp(Packet.m_pData, VERSIONSRV_GETVERSION, sizeof(VERSIONSRV_GETVERSION)) == 0)
++ {
++ SendVer(&Packet.m_Address);
++ }
++
++ if(Packet.m_DataSize == sizeof(VERSIONSRV_GETMAPLIST) &&
++ mem_comp(Packet.m_pData, VERSIONSRV_GETMAPLIST, sizeof(VERSIONSRV_GETMAPLIST)) == 0)
++ {
++ CNetChunk p;
++ p.m_ClientID = -1;
++ p.m_Address = Packet.m_Address;
++ p.m_Flags = NETSENDFLAG_CONNLESS;
++
++ for(int i = 0; i < m_NumPackets; i++)
++ {
++ p.m_DataSize = m_aPackets[i].m_Size;
++ p.m_pData = &m_aPackets[i].m_Data;
++ g_NetOp.Send(&p);
++ }
++ }
++ }
++
++ // be nice to the CPU
++ thread_sleep(1);
++ }
++
++ return 0;
++}
+diff --git a/src/versionsrv/versionsrv.h b/src/versionsrv/versionsrv.h
+new file mode 100644
+index 0000000..46f6425
+--- /dev/null
++++ b/src/versionsrv/versionsrv.h
+@@ -0,0 +1,19 @@
++/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
++/* If you are missing that file, acquire a complete release at teeworlds.com. */
++#ifndef VERSIONSRV_VERSIONSRV_H
++#define VERSIONSRV_VERSIONSRV_H
++static const int VERSIONSRV_PORT = 8302;
++
++struct CMapVersion
++{
++ char m_aName[8];
++ unsigned char m_aCrc[4];
++ unsigned char m_aSize[4];
++};
++
++static const unsigned char VERSIONSRV_GETVERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 'g'};
++static const unsigned char VERSIONSRV_VERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 's'};
++
++static const unsigned char VERSIONSRV_GETMAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 'g'};
++static const unsigned char VERSIONSRV_MAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 's'};
++#endif
+--
+2.1.0
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 00000000000..e01a5f135cd
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,48 @@
+# Maintainer: josephgbr <rafael.f.f1@gmail.com>
+
+pkgname=teeworlds-hunter
+pkgver=0.6.2.r6.944705e
+pkgrel=1
+pkgdesc="A customized server by bluelightzero where a Tee hunts everyone else"
+arch=('i686' 'x86_64')
+url="https://www.teeworlds.com/forum/viewtopic.php?id=10408"
+license=('custom')
+depends=('gcc-libs')
+makedepends=('python' 'bam' 'mesa' 'unzip')
+source=(git+https://github.com/bluelightzero/$pkgname.git
+ 0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch
+ server.sh.in
+ README)
+md5sums=('SKIP'
+ 'a76fcf155a7a38a51d95ad1ff0050472'
+ '529146a3b13993770fa414f66c67fa26'
+ '81785338a2084be394a997a465e33027')
+
+pkgver() {
+ cd $pkgname
+ v=`grep "GAME_VERSION " src/game/version.h | cut -d\" -f2`
+ r=`git rev-list --count HEAD`
+ h=`git rev-parse --short HEAD`
+ echo $v.r$r.$h
+}
+
+prepare() {
+ cd $pkgname
+ git apply ../0001-Enabled-mastersrv-and-versionsrv-as-they-are-require.patch || true
+}
+
+build() {
+ cd $pkgname
+ bam server_release
+}
+
+package() {
+ cd $pkgname
+ install -Dm755 teeworlds_srv "$pkgdir"/usr/share/$pkgname/teeworlds_srv
+ install -Dm644 SERVER_CFG/hunter.cfg "$pkgdir"/usr/share/$pkgname/server.cfg
+ install -Dm644 license.txt "$pkgdir"/usr/share/licenses/$pkgname/license
+ install -Dm644 ../README "$pkgdir"/usr/share/doc/$pkgname/README
+
+ install -Dm755 ../server.sh.in "$pkgdir"/usr/bin/${pkgname}_srv
+ sed -i "s/@MODNAME@/$pkgname/" "$pkgdir"/usr/bin/${pkgname}_srv
+}
diff --git a/README b/README
new file mode 100644
index 00000000000..a2aed8e840f
--- /dev/null
+++ b/README
@@ -0,0 +1,57 @@
+=========Hunter Server Mod===========
+
+The hunter vs everyone else.
+
+*IMPORTANT*: Please return frequently for updates as this mod is new and bugs may be discovered.
+
+Rules:
+
+- 1 player is randomly selected to be the 'Hunter'
+- The other players then have to work out who this player is and kill them.
+- Friendly fire is always on. It is possible to mistake a normal player with the hunter.
+- All Deaths are anonymous, it will not show who killed who. This does not stop players saying it in chat, but they might lie!
+- If the hunter dies then everyone else wins.
+- If the hunter is last one left then hunter wins.
+- The hunter has different Weapon stats than normal players
+
+Differences:
+
+- Hunter has hammer, normal players don't. Hammer is instant kill.
+- Hunter's handgun does x2 damage
+- Hunter's shotgun can kill in 2 close shots
+- Hunter's lazer can kill in 2 close shots
+- Hunter's Grenade does not self harm (allowing many rocket jumps) and has visible debris.
+
+Source:
+
+https://github.com/bluelightzero/teeworlds-hunter
+
+Compiling:
+
+- Follow normal instructions
+- Use bam server_release
+
+Bug report:
+
+- Please report any bugs to my github page.
+- please be aware that some bugs may exist, I have been testing this all day with friends and It is pretty solid.
+
+IMPORTANT keep this option the same:
+
+sv_tournament_mode 1
+
+Recommended cfg:
+
+sv_name "Hunter CTF5 *NEW*"
+sv_motd "A random player is secretly selected to be hunter. Hunter vs Everyone else. Hunter is stronger."
+sv_map ctf5
+sv_gametype mod
+sv_max_clients 16
+sv_register 1
+sv_tournament_mode 1
+
+Note for map makers:
+
+- Place extra hearts and shields to help game last longer
+- Try not to use death tiles, players have only 1 life.
+- Place lasers in harder to reach places, a hunter can use them to quickly kill off normal players so putting it in easy reach can cause a camping issue.
diff --git a/server.sh.in b/server.sh.in
new file mode 100644
index 00000000000..21a4744c7f4
--- /dev/null
+++ b/server.sh.in
@@ -0,0 +1,12 @@
+#/bin/sh
+# Run the teeworlds modified server "@MODNAME@" with its config file
+
+bin=/usr/share/@MODNAME@/teeworlds_srv
+cfg=/usr/share/@MODNAME@/server.cfg
+
+if [ ! -z "$1" -a "$1" == "-f"]; then
+ $bin -f $2 # expects config file in "$2
+else
+ $bin -f $cfg
+fi
+