gckmsd/masterserver.py

141 lines
4.9 KiB
Python
Raw Normal View History

2019-06-18 12:20:49 +02:00
import logging
2019-01-15 23:22:43 +01:00
import socket
import threading
import datetime
import time
import traceback
2019-06-18 12:20:49 +02:00
# List of servers always up
DEDICATED_SERVERS = [{"ip": "136.143.97.184", "port": 19711, "last": datetime.datetime.now()},
{"ip": "73.181.147.35", "port": 19711, "last": datetime.datetime.now()},
{"ip": "artolsheim.hipstercat.fr", "port": 19711, "last": datetime.datetime.now()}]
SERVERS = DEDICATED_SERVERS.copy()
def setup_logger(name):
# setup logger
_ch = logging.StreamHandler()
_ch.setLevel("DEBUG")
_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
_ch.setFormatter(_formatter)
logger = logging.getLogger(name)
logger.addHandler(_ch)
logger.setLevel("DEBUG")
logger.debug("Logger initialized")
return logger
logger = setup_logger(__name__)
2019-01-15 23:22:43 +01:00
def main():
listen_ip = "0.0.0.0"
2019-06-18 12:20:49 +02:00
# register socket. dedicated servers send packets to this socket to register.
2019-01-15 23:22:43 +01:00
registersock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
registersock.bind((listen_ip, 27900))
2019-06-18 12:20:49 +02:00
logger.info("Register server listening on %s UDP %s", listen_ip, 27900)
2019-01-15 23:22:43 +01:00
2019-06-18 12:20:49 +02:00
# query socket. clients use this socket to query available servers.
2019-01-15 23:22:43 +01:00
querysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2019-02-26 15:19:43 +01:00
querysock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
2019-01-15 23:22:43 +01:00
querysock.bind((listen_ip, 28900))
2019-06-18 12:20:49 +02:00
print("Query server listening on %s TCP %s", listen_ip, 28900)
2019-01-15 23:22:43 +01:00
querysock.listen(5)
2019-06-18 12:20:49 +02:00
# everything is threaded.
2019-01-15 23:22:43 +01:00
registerthread = threading.Thread(target=registerloop, args=(registersock,))
querythread = threading.Thread(target=queryloop, args=(querysock,))
2019-06-18 12:20:49 +02:00
# clean thread is used to clean older server that haven't updated in a while
2019-01-15 23:22:43 +01:00
cleanthread = threading.Thread(target=cleanloop)
2019-06-18 12:20:49 +02:00
# start everything
2019-01-15 23:22:43 +01:00
querythread.start()
registerthread.start()
cleanthread.start()
2019-06-18 12:20:49 +02:00
# wait until everything is finished (which will never happen because they are all running in a loop)
2019-01-15 23:22:43 +01:00
querythread.join()
registerthread.join()
cleanthread.join()
def queryloop(querysock):
while True:
2019-06-18 12:20:49 +02:00
# new query incoming from a client
2019-01-15 23:22:43 +01:00
(clientsocket, (_, _)) = querysock.accept()
2019-06-18 12:20:49 +02:00
logger.info("Query from %s:%s", clientsocket[0], clientsocket[1])
# forge the response
2019-01-15 23:22:43 +01:00
b = b''
for i in range(len(SERVERS)):
2019-04-22 23:12:02 +02:00
ip = SERVERS[i]["ip"]
2019-01-15 23:22:43 +01:00
b += "{:02d}".format(i).encode("ascii")
b += b'\xac'
2019-04-22 23:12:02 +02:00
b += ip.encode("ascii")
2019-01-15 23:22:43 +01:00
b += b'\xac'
b += str(SERVERS[i]["port"]).encode("ascii")
2019-02-12 14:57:55 +01:00
b += b'\x00'
2019-06-18 12:20:49 +02:00
logger.info("Sending back %s", b.decode('utf8'))
# send it
2019-01-15 23:22:43 +01:00
clientsocket.send(b)
2019-06-18 12:20:49 +02:00
# close the socket
clientsocket.close()
2019-01-15 23:22:43 +01:00
def registerloop(registersock):
while True:
2019-06-18 12:20:49 +02:00
# new register incoming from a server
2019-01-15 23:22:43 +01:00
data, (ip, port) = registersock.recvfrom(1024)
try:
2019-06-18 12:20:49 +02:00
# first byte is some kind of opcode, so we leave it. the rest is the game port
2019-02-12 17:24:58 +01:00
gameport = data[1:].decode("ascii")
if int(gameport):
2019-06-18 12:20:49 +02:00
# game port sent. now we try to reach the server to avoid registering of servers unreachable.
2019-02-12 17:24:58 +01:00
tempsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tempsocket.settimeout(5)
2019-06-18 12:20:49 +02:00
# we try to send a "status" packet to the server.
2019-02-12 17:24:58 +01:00
tempsocket.sendto("\\status\\".encode("ascii"), (ip, port))
tempsocket.recvfrom(1024)
2019-06-18 12:20:49 +02:00
# server will be None if it has not been seen before, or something else if it has been seen
2019-02-12 17:24:58 +01:00
server = next((x for x in SERVERS if x["ip"] == ip and x["port"] == gameport), None)
if not server:
SERVERS.append({"ip": ip, "port": gameport, "last": datetime.datetime.now()})
2019-06-18 12:20:49 +02:00
logger.info("New server: %s:%s" % (ip, gameport))
2019-01-15 23:22:43 +01:00
else:
2019-02-12 17:24:58 +01:00
server["last"] = datetime.datetime.now()
2019-06-18 12:20:49 +02:00
logger.info("Keepalive from: %s:%s" % (ip, gameport))
2019-02-12 17:24:58 +01:00
tempsocket.close()
2019-01-15 23:22:43 +01:00
else:
2019-06-18 12:20:49 +02:00
# server sent shit, discard it
2019-02-12 17:24:58 +01:00
print("Fuck it, wasn't int: ", data)
2019-01-15 23:22:43 +01:00
except Exception as e:
2019-06-18 12:20:49 +02:00
# something wrong happened but we don't know what
2019-01-15 23:22:43 +01:00
print("Exception")
traceback.print_exc()
2019-06-18 12:20:49 +02:00
2019-01-15 23:22:43 +01:00
def cleanloop():
2019-06-18 12:20:49 +02:00
# clean loop removes servers that haven't registered or sent a packet in the last 2 minutes
# but don't remove known dedicated servers
2019-01-15 23:22:43 +01:00
while True:
now = datetime.datetime.now()
for server in SERVERS:
2019-06-18 12:20:49 +02:00
if now > server["last"] + datetime.timedelta(minutes=2) and server not in DEDICATED_SERVERS:
logger.info("Deleting %s:%s" % (server["ip"], server["port"]))
2019-01-15 23:22:43 +01:00
try:
SERVERS.remove(server)
except ValueError:
pass
time.sleep(10)
if __name__ == '__main__':
2019-06-18 12:20:49 +02:00
main()