Last active
December 20, 2015 01:09
-
-
Save b1naryth1ef/6047079 to your computer and use it in GitHub Desktop.
This is a script for managing Quake 3 (ioUrT) instances over stdin/stdout and Redis. I built this for use with a hosting platform I am working on. Due to the very unreliable system that RCON is based on, it simply wasn't an option to just use RCON. This system has the advantage of speed, write reliability, with the downside of write-read (respon…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os, subprocess, redis, thread, json, time, daemon, re | |
red = redis.Redis("localhost", password=os.getenv("REDISPASS")) | |
class InterCom(object): | |
def __init__(self, id): | |
self.id = id | |
self.prefix = "server-%s-" % self.id | |
def getCvar(self, i): | |
def conf(i): | |
res = re.findall('".*?" is:"(.*?)\^7"', i) | |
if len(res): | |
return True | |
return False | |
i = self.get(i, need=conf) | |
if not i: return None | |
res = re.findall('".*?" is:"(.*?)\^7"', i[0]['msg']) | |
if len(res): | |
return res[0] | |
def get(self, cmd, lines=1, end=None, need=None): | |
""" | |
This is complicated as dicks, and probablly not perfect. | |
It's entirely Quake3's fault for not offering decent support for a tool | |
like rcon. I'm gonna fucking make sure UrTHD has decent rcon/socket support. | |
GODDAMNIT! | |
""" | |
i = 0 # Thread Retries | |
value = [] # We need an object so it can be set globally, list/dict works | |
running = [] | |
def resp(a): # Function to pass to readLoop | |
if need: | |
if need(a['msg']): | |
value.append(a) | |
running.append(False) | |
return False | |
return True | |
value.append(a) | |
if len(value) >= lines: | |
running.append(False) | |
return False | |
if end(a['msg']): | |
running.append(False) | |
return False | |
return True | |
thread.start_new_thread(self.readLoop, (resp,)) | |
self.cmd(cmd) | |
while not len(running): | |
time.sleep(0.05) | |
i += 1 | |
if i > 25: return None | |
return value | |
def readLoop(self, f, *args): | |
sub = red.pubsub() | |
sub.subscribe(self.prefix+"read") | |
looping = True | |
for l in sub.listen(): | |
if l['type'] == "message": | |
msg = json.loads(l['data']) | |
looping = (f(msg, *args) != False) | |
if not looping: | |
sub.unsubscribe(self.prefix+"read") | |
return True | |
def quit(self): | |
red.rpush(self.prefix+"write", json.dumps({"action": "quit"})) | |
def restart(self): | |
red.rpush(self.prefix+"write", json.dumps({"action": "restart"})) | |
def cmd(self, cmd): | |
red.rpush(self.prefix+"write", json.dumps({ | |
"action": "write", | |
"content": cmd | |
})) | |
class Process(object): | |
def __init__(self, id, exe, config="server.cfg", port=27960): | |
self.id = id | |
self.exe = exe | |
self.config = config | |
self.port = port | |
self.running = False | |
self.proc = None | |
self.prefix = "server-%s-" % self.id | |
def start(self): | |
if self.running: return | |
cmd = "%s +set net_port %s +exec %s +set dedicated 2" % (self.exe, self.port, self.config) | |
self.proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
self.running = True | |
thread.start_new_thread(self.writeToThread, ()) | |
thread.start_new_thread(self.readFromThread, ()) | |
self.proc.wait() | |
self.running = False | |
return | |
def stop(self, new=False): | |
if not self.running: return | |
self.proc.kill() | |
def push(self, t, m): | |
red.publish(self.prefix+"read", json.dumps({ | |
"type": t, | |
"time": time.time(), | |
"sid": self.id, | |
"msg": m})) | |
# Write stuff into our process | |
def writeToThread(self): | |
while self.running: | |
msg = red.blpop(self.prefix+"write") | |
try: | |
msg = json.loads(msg[1]) | |
if msg['action'] == "write": | |
self.proc.stdin.write(msg['content']+"\n") | |
elif msg['action'] == "quit": | |
self.stop() | |
elif msg['action'] == "restart": | |
self.stop(new=True) | |
except Exception, e: | |
print "Error loading message %s (%s)" % (str(msg), e) | |
continue | |
# Read stuff from our process | |
def readFromThread(self): | |
while self.running: | |
line = self.proc.stdout.readline().rstrip() | |
print line | |
self.push("raw", line) | |
self.push("action", "exit") | |
if __name__ == "__main__": | |
# with daemon.DaemonContext(): | |
p = Process(1, "~/Downloads/UrbanTerror42/Quake3-UrT-Ded.x86_64") | |
p.start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment