aboutsummaryrefslogtreecommitdiffstats
path: root/pycrctrl.py
diff options
context:
space:
mode:
authorFulgen301 <tokmajigeorge@gmail.com>2017-11-25 15:58:21 +0100
committerFulgen301 <tokmajigeorge@gmail.com>2017-11-25 15:58:21 +0100
commit44fe3a789328c612bc7cc175ab524c2cfc968783 (patch)
treeec5cdb80b8335b8c7e5df064f9f8b8530dddd343 /pycrctrl.py
parent5ad36f10bf4e2d1a2691b7afcc3b49eee4f81a6e (diff)
parente08aab2b2f5bac8682ae5a4a6134122595ed5343 (diff)
downloadpycrctrl-44fe3a789328c612bc7cc175ab524c2cfc968783.tar.gz
pycrctrl-44fe3a789328c612bc7cc175ab524c2cfc968783.zip
Merge branch 'IRC'
# Conflicts: # pycrctrl.py
Diffstat (limited to 'pycrctrl.py')
-rwxr-xr-x[-rw-r--r--]pycrctrl.py489
1 files changed, 226 insertions, 263 deletions
diff --git a/pycrctrl.py b/pycrctrl.py
index ae637a2..88ec029 100644..100755
--- a/pycrctrl.py
+++ b/pycrctrl.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#Copyright (c) 2017, George Tokmaji
@@ -14,33 +15,33 @@
#ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-import sys
import os
+sys = os.sys
+import socket
import subprocess
import queue
-from _thread import start_new_thread
+import threading
from time import sleep
from platform import architecture
import re
import configparser
from io import BytesIO
-import logging
-
import urllib.request
-try:
- import supybot.ircmsgs as ircmsgs
-except ImportError:
- ircmsgs = None
-
import random
+import traceback
import tarfile
from enum import IntEnum
from gzip import GzipFile
+import asyncio
+from blinker import signal
+from asyncirc import irc
+import asyncirc.plugins.addressed
+
#
# Helpers
#
@@ -66,13 +67,6 @@ class list(list):
def isEmpty(self):
return len(self) == 0
-class CmdResult(IntEnum):
- UnknownCommand = -1
- Success = 0
- SyntaxFail = 1
- RightsFail = 2
- RuntimeError = 3
-
class Updater(object):
parent = None
__current_revision = ""
@@ -80,12 +74,8 @@ class Updater(object):
def __init__(self, parent):
self.parent = parent
- try:
- with open(os.path.join(self.parent.path, "snapshot.id"), "rb") as fobj:
- self.__current_revision = fobj.read().decode("utf-8")
- except OSError:
- pass
-
+ with open(os.path.join(self.parent.path, "snapshot.id"), "rb") as fobj:
+ self.__current_revision = fobj.read().decode("utf-8")
start_new_thread(self.checkForUpdates, ())
@property
@@ -107,66 +97,67 @@ class Updater(object):
def checkForUpdates(self):
while True:
try:
- site = urllib.request.urlopen("http://openclonk.org/nightly-builds").read().decode("utf-8").split("<a href='/builds/nightly/snapshots/")
+ site = urllib.request.urlopen(self.parent.config["Addresses"]["snapshotList"]).read().decode("utf-8").split(self.parent.config["Updater"]["SplitString"])
site.remove(site[0])
site = [i.split("' title")[0] for i in site]
x = None
for i in site:
- x = re.match(r"openclonk-snapshot-(.*)-(.*)-{}-{}-.*".format(sys.platform, self.lookuptable[architecture()[0]]), i)
+ x = re.match(self.parent.config["RegExps"]["Snapshot"].format(sys.platform, self.lookuptable[architecture()[0]]), i)
if x:
rev = x.group(2)
if self.current_revision != rev:
self.current_revision = rev
- self.loadNewSnapshot(i)
+ self.loadNewSnapshot(x)
break
if not x:
- self.parent.log.error("Updater.checkForUpdates: Regular expression doesn't match!")
+ print("Updater.checkForUpdates: Regular expression doesn't match!", file=sys.stderr)
except Exception as e:
- self.parent.log.error(str(e.args[0]))
+ traceback.print_exc()
finally:
sleep(10)
- def loadNewSnapshot(self, f):
- self.parent.log.info("Downloading snapshot with id {}".format(self.current_revision))
+ def loadNewSnapshot(self, reg):
+ print(f"Updater: Downloading snapshot with id {self.current_revision}")
with open(os.path.join(self.parent.path, "snapshot"), "wb") as fobj:
- fobj.write(urllib.request.urlopen("http://openclonk.org/builds/nightly/snapshots/{}".format(f)))
+ fobj.write(urllib.request.urlopen(self.parent.config["Addresses"]["snapshotDownload"].format(reg.group(0).split("' title")[0])).read())
#extract the snapshot
tar = tarfile.open(os.path.join(self.parent.path, "snapshot"), mode="r:bz2")
tar.extractall(path=self.parent.path)
- self.parent.log.info("New snapshot has been extracted.")
+ print("Updater: New snapshot has been extracted.")
#get the openclonk-server autobuild
- site = json.loads(urllib.request.urlopen("https://autobuild.openclonk.org/api/v1/jobs").read().decode("utf-8"))
+ site = json.loads(urllib.request.urlopen(self.parent.config["Addresses"]["autobuildList"]).read().decode("utf-8"))
for commit in site:
for build in commit["builds"]:
if re.match(r"{}-{}-.*".format(sys.platform, self.lookuptable[architecture()[0]]), build["platform"]["triplet"]):
for b in build["components"]:
- reg = re.match(r".*/openclonk-server-(.*)-(.*)-(.*)-.*", str(b["path"])) #skip the engine check as the only useful one is openclonk-server
+ reg = re.match(self.parent.config["RegExps"]["Autobuild"], str(b["path"])) #skip the engine check as the only useful one is openclonk-server
if reg and (reg.group(1), reg.group(2), reg.group(3)) == (self.current_revision[:-3], sys.platform, self.lookuptable[architecture()[0]]):
- self.parent.log.info("Downloading autobuild with id {}".format(self.current_revision))
+ print(f"Updater: Downloading autobuild with id {self.current_revision}")
buffer = BytesIO()
- buffer.write(urllib.request.urlopen("https://autobuild.openclonk.org/static/binaries/{}".format(b["path"])).read())
+ buffer.write(urllib.request.urlopen(self.parent.config["Addresses"]["autobuildDownload"].format(b["path"])).read())
buffer.seek(0)
- with open(os.path.join(self.parent.path, self.parent.config["Clonk"]["Engine"]), "wb") as fobj:
+ with open(os.path.join(self.parent.path, self.parent.config["Updater"]["BinaryName"]), "wb") as fobj:
fobj.write(GzipFile(fileobj=buffer).read())
- self.parent.log.info("New openclonk-server build has been extracted.")
- os.chmod(
- os.path.join(self.parent.path, self.parent.config["Clonk"]["Engine"]),
- os.stat(os.path.join(self.parent.path, self.parent.config["Clonk"]["Engine"])).st_mode | 64)
+ print("Updater: New openclonk-server build has been extracted.")
+ os.chmod(os.path.join(self.parent.path, self.parent.config["Updater"]["BinaryName"]), os.stat(os.path.join(self.parent.path, self.parent.config["Updater"]["BinaryName"])).st_mode | 64)
return True
+
+
class PyCRCtrl(object):
"""Server control"""
+ irc = None
clonk = None
- thread_started = False
+ server_thread = None
stopped = False
config = configparser.ConfigParser()
@@ -184,8 +175,6 @@ class PyCRCtrl(object):
__ingamechat = "aktiviert"
updater = None
- log = None
- logfile = None
shutdowned = False
@property
@@ -208,14 +197,12 @@ class PyCRCtrl(object):
self.__ingamechat = text
self.state = self.state
- def __init__(self, irc=None, path=".", config="pycrctrl.ini"):
- if ircmsgs is not None:
- self.irc = irc
+ def __init__(self, path, config="pycrctrl.ini"):
self.path = path
self.loadConfigFile(config)
- self.setupLog()
- self.__ingamechat = "aktiviert" if self.config["Clonk"].getboolean("Autohost") else "deaktiviert" # important because there is no scenario hostet yet
+ self.ingamechat = "aktiviert" if self.config["IRC"].getboolean("Ingamechat") else "deaktiviert"
self.loadScenarioList()
+ self.startIRC()
self.queue = queue.Queue(5)
if self.config["Updater"].getboolean("Enabled"):
@@ -226,29 +213,18 @@ class PyCRCtrl(object):
raise OSError("No path specified")
with open(os.path.join(self.path,"scenarios.lst"), "r") as fobj:
- self.scenlist = [line.strip() for line in fobj.readlines()]
+ self.scenlist = fobj.readlines()
with open(os.path.join(self.path, "scenarios_league.lst"), "r") as fobj:
- self.league_scenlist = [line.strip() for line in fobj.readlines()]
+ self.league_scenlist = fobj.readlines()
- self.log.debug("Scenario lists loaded.")
+ print("DEBUG: Scenario lists loaded.")
- def setupLog(self) -> None:
- if self.log:
- return
-
- self.log = logging.getLogger(type(self).__name__)
- self.log.setLevel(getattr(logging, self.config["Logging"]["Level"], logging.INFO))
-
- if not self.log.handlers:
- ch = logging.FileHandler(
- os.path.join(self.path, self.config["Logging"]["File"])
- )
- ch.setLevel(getattr(logging, self.config["Logging"]["Level"], logging.INFO))
- ch.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s"))
-
- self.log.addHandler(ch)
- self.log.info("PyCRCtrl started.")
+ def startIRC(self) -> None:
+ asyncirc.plugins.addressed.register_command_character(self.config["General"]["Prefix"])
+ self.irc = irc.connect(self.config["IRC"]["Server"], self.config["IRC"]["Port"], use_ssl=self.config["IRC"].getboolean("SSL"))
+ self.irc.register(*([self.config["IRC"]["Nick"]] * 3), password=self.config["IRC"]["Password"])
+ self.irc.join([*(list(self.config["Channels"].values())), "#openclonk-atlantis"])
def loadConfigFile(self, config) -> None:
if self.path == None:
@@ -265,24 +241,36 @@ class PyCRCtrl(object):
elif not os.path.exists(conf):
c = """[General]
-UseLogfile=true
Prefix=@
[Clonk]
Engine=clonk
Encoding=utf-8
-Commandline=/fullscreen /lobby:300 /config:config.txt /record /faircrew
+Commandline=/fullscreen /lobby:300 /record /faircrew
Autohost=false
[IRC]
-Ingamechat=true
-
- [Channels]
- Parent="#clonk-SGGP"
- Ingame="#clonk-SGGP-ingame"
+Ingamechat=false
+Nick=PyCRCtrl
+Password=
+Server=irc.euirc.net
+Port=6667
+SSL=false
+
+[Channels]
+Parent=
+Ingame=
[Updater]
Enabled=false
+BinaryName=
+SplitString=
+
+[Addresses]
+snapshotList=http://openclonk.org/nightly-builds
+snapshotDownload=http://openclonk.org/builds/nightly/snapshots/{}
+autobuildList=https://autobuild.openclonk.org/api/v1/jobs
+autobuildAddress=https://autobuild.openclonk.org/static/binaries/{}
[RegExps]
LobbyStart=((?:Los geht's!|Action go!)\\s*)
@@ -318,65 +306,40 @@ Snapshot=openclonk-snapshot-(.*)-(.*)-{}-{}-."""
encoding=self.config["Clonk"]["Encoding"]
)
self.state = "Lobby"
- self.log.info("Scenario: {}".format(self.scenario))
self.readServerOutput()
if self.config["Clonk"].getboolean("Autohost") == False:
- self.thread_started = False
+ self.server_thread = None
self.setTopic("Kein laufendes Spiel.")
break
finally:
if self.clonk:
self.clonk.stdin.close()
-
- return (CmdResult.Success, "")
def readServerOutput(self):
while True:
try:
output = self.clonk.stdout.readline()
+
if re.match(self.config["RegExps"]["Shutdown"], output):
self.clonk.stdin.close()
elif output == "" and self.clonk.poll() is not None:
if self.clonk:
- self.log.debug("poll() is not None, shutting down")
self.clonk.stdin.close()
self.clonk = None
self.scenario = ""
return
elif output:
- output = output.splitlines()[0]
- #output = output.decode("utf-8").splitlines()[0]
- output = output[(output.find("] ") if output.find("] ") != -1 else -2)+len("] "):]
+ output = output.strip()
+ output = output[(output.find("] ") if output.find("] ") != -1 else -2) + len("] "):] # TODO: Make this code readable
if output[0] == ">":
output = output[1:]
- self.log.info(output)
- part = self.isMessage(output)
- if part and part.group(0) != self.irc.nick:
- #self.log.info(output)
- cmd = part.group(3).split(" ", 1)
- found = False
- x = None
- try:
- if len(cmd) > 0:
- key = cmd[0].splitlines()[0]
- if not hasattr(self, key):
- raise RuntimeError
- try:
- x = getattr(self, key)(cmd[1].split(" "), user=part.group(1))
- except IndexError:
- x = getattr(self, key)(user=part.group(1))
- break
-
- except RuntimeError:
- self.writeToServer('Unbekannter Befehl: "' + part.group(2) + '"!')
- if x:
- if type(x) == tuple and x[1] != "":
- self.writeToServer(x[1])
- del x
+ print(output)
+
+ self.checkForCommands(output, reply=self.writeToServer)
if re.match(self.config["RegExps"]["LobbyStart"], output):
self.state = "Lädt"
@@ -384,13 +347,12 @@ Snapshot=openclonk-snapshot-(.*)-(.*)-{}-{}-."""
elif re.match(self.config["RegExps"]["Start"], output):
self.state = "Läuft"
- if self.irc and self.ingamechat == "aktiviert":
- if output.find("<" + self.irc.nick + ">") == -1:
- if self.isMessage(output) and "[IRC]" not in output and self.config["General"]["Prefix"] not in output:
- self.irc.reply("[Clonk]{}".format(output), to=self.config["Channels"]["Ingame"])
+ if self.ingamechat == "aktiviert":
+ if self.isMessage(output) and f"<{self.irc.nickname}>" not in output:
+ self.irc.say(self.config["Channels"]["Ingame"], f"[Clonk]{output}")
- elif any((re.match(self.config["RegExps"]["PlayerJoin"], output), re.match(self.config["RegExps"]["PlayerLeave"], output))):
- self.irc.reply(output, to=self.config["Channels"]["Ingame"])
+ elif (re.match(self.config["RegExps"]["PlayerJoin"], output) or re.match(self.config["RegExps"]["PlayerLeave"], output)):
+ self.irc.say(self.config["Channels"]["Ingame"], output)
except KeyboardInterrupt:
@@ -398,137 +360,36 @@ Snapshot=openclonk-snapshot-(.*)-(.*)-{}-{}-."""
self.clonk.stdin.close()
except Exception as e:
- self.log.exception(e.args[0])
+ traceback.print_exc()
continue
-
- return (CmdResult.Success, "")
-
- def doPrivmsg(self, msg):
- if not (self.irc and self.ingamechat == "aktiviert"):
- return
- for channel in msg.args[0].split(","):
- if channel == self.config["Channels"]["Ingame"] and msg.nick != self.irc.nick:
- self.writeToServer("[IRC]<{}> {}".format(msg.nick, msg.args[1]))
-
- #
- # Commands
- #
-
- def host(self, scenario=None, user=None) -> str:
- if not scenario:
- return (CmdResult.SyntaxFail, "Bitte gib einen Szenarionamen an!")
- if hasattr(scenario, "decode"):
- try:
- scenario = scenario.decode(self.config["Clonk"]["Encoding"])
- except:
- self.log.warning("Unable to decode {}".format(scenario))
- return (CmdResult.RuntimeError, "Dekodierfehler. Bitte kontaktiere den Hoster dieses Servers.")
-
- scenario = scenario.splitlines()[0]
- if scenario == "random":
- scenario = random.choice(self.scenlist).splitlines()[0]
-
- elif scenario not in self.scenlist:
- return (CmdResult.SyntaxFail,'Szenario "{}" wurde nicht gefunden!'.format(scenario))
-
- if self.thread_started == False:
- self.scenario = scenario
- self.thread_started = True
- start_new_thread(self.startClonk,())
- return (CmdResult.Success, 'Szenario "{}" wird jetzt gehostet.'.format(scenario))
-
- if not self.queue.full():
- self.queue.put(scenario)
- return (CmdResult.Success, 'Szenario "{}" wurde der Warteschlange hinzugefügt.'.format(scenario))
- else:
- return (CmdResult.RuntimeError, "Die Warteschlange ist voll!")
-
- def start(self, time=None, user=""):
- """Startet das Spiel."""
- self.stopped = False
- if time:
- try:
- time = int(time[0])
- except:
- time = 5
- self.writeToServer("/start {}".format(time))
- else:
- self.writeToServer("/start")
-
- return (CmdResult.Success, "")
-
-
- def stop(self, prm=None, user=""):
- """Stoppt den Countdown."""
- def stopping(self):
- while self.clonk and self.stopped:
- self.writeToServer("/start 60000")
- if self.stopped == False:
- return
- sleep(100)
-
- if self.stopped == False:
- self.stopped = True
- start_new_thread(stopping, (self,))
-
- return (CmdResult.Success, "")
-
-
- def help(self, prm=None, user=""):
- """Gibt die Hilfe aus."""
- self.writeToServer("Verfügbare Befehle:")
- for text, function in self.commands.items():
- self.writeToServer("{} -- {}".format(text, function.__doc__))
-
- return (CmdResult.Success, "")
-
- def displayQueue(self, prm=None, user=""):
- """Gibt die Warteschlange aus."""
- self.writeToServer("Warteschlange:")
-
- for i,scen in enumerate(self.queue.queue):
- self.writeToServer("{}. {}".format(i+1, scen))
-
- return (CmdResult.Success, "")
-
-
- def list(self, prm=None, user=""):
- """Zeigt die Szenarioliste an."""
- self.writeToServer("Szenarienlist:\n-------------")
- for scen in self.scenlist:
- self.writeToServer(scen + ("(Liga)" if scen in self.league_scenlist else ""))
-
- return (CmdResult.Success, "")
-
- def ircCommands(self, prm=None, user=""):
- """Enthält Befehle zur Steuerung der IRC-Funktionen."""
- if not prm:
- return (CmdResult.SyntaxFail, "")
-
- if prm[0] == "ingamechat":
- if prm[1] == "off":
- self.ingamechat = "deaktiviert"
- elif prm[1] == "on":
- self.ingamechat = "aktiviert"
-
- return (CmdResult.Success, "")
-
- #
- # Methods
- #
+ def checkForCommands(self, msg, reply) -> None:
+ part = self.isCommand(msg)
+ if part and part[1] != self.irc.nick:
+ cmd = part[2].split(" ", 1)
+ found = False
+ if len(cmd) > 0:
+ key = cmd[0].strip()
+
+ if signal(f"cmd-{key}").receivers:
+ found = True
+
+ msg = [reply]
+ msg.extend(cmd[1].strip().split(" ") if len(cmd) > 1 else [])
+
+ signal(f"cmd-{key}").send(msg)
+
+ if not found:
+ reply('Unbekannter Befehl: "' + part[2] + '"!')
def isMessage(self, msg):
- return re.match(self.config["RegExps"]["Message"].format(prefix=self.config["General"]["Prefix"]), msg)
-
- def getMessageNick(self, msg):
- m = self.isMessage(msg)
- if m:
- return m.group(1)
+ if self.isCommand(msg):
+ return
+
+ return re.match(self.config["RegExps"]["Message"], msg)
- def addCommand(self, function, text):
- self.commands[text.split(" ")[0]] = function
- return self
+ def isCommand(self, msg):
+ return re.match(self.config["RegExps"]["Command"].format(prefix=self.config["General"]["Prefix"]), msg)
def addScenario(self, link):
name = ""
@@ -547,43 +408,27 @@ Snapshot=openclonk-snapshot-(.*)-(.*)-{}-{}-."""
self.scenlist.append(name)
return self
- def writeToServer(self, text=None) -> tuple:
- if text == None and self.clonk == None:
- return (CmdResult.RuntimeError, "")
- elif type(text) != str:
- try:
- text = text.decode(self.config["Clonk"]["Encoding"])
- except:
- raise IOError("Cannot write anything else to the server except the following data types: bytes, str (got {})".format(type(text).__name__))
+ def writeToServer(self, text : str) -> None:
+ if not self.clonk:
+ return
- if self.clonk and self.clonk.stdin:
- self.clonk.stdin.write(text + "\n")
+ elif self.clonk.stdin:
+ self.clonk.stdin.write(f"{text}\n")
self.clonk.stdin.flush()
- return (CmdResult.Success, "")
def setTopic(self, text=None) -> None:
if not self.irc:
return
- if type(text) != str:
- try:
- text = text.decode(self.config["Clonk"]["Encoding"])
- except:
- raise TypeError("bytes or str expected, got {}".format(type(text).__name__))
-
if self.topic != text:
self.topic = text
channel = self.config["Channels"]["Ingame"]
- if not channel.startswith("#"):
- raise ValueError("Invalid channel: {} (ident: {})".format(channel, channel == "#clonk-SGGP-ingame"))
- self.irc.sendMsg(ircmsgs.topic(self.config["Channels"]["Ingame"], text))
+ self.irc.writeln(f"TOPIC {self.config['Channels']['Ingame']} :{text}")
def shutdown(self):
if self.shutdowned:
return
- self.log.info("Shutting down...")
-
del self.updater
if self.clonk and self.clonk.stdin and not self.clonk.stdin.closed:
@@ -591,7 +436,7 @@ Snapshot=openclonk-snapshot-(.*)-(.*)-{}-{}-."""
with open(self.config_path, "w") as fobj:
self.config.write(fobj)
- self.log.debug("Config file saved.")
+ print("Config file saved.")
with open(os.path.join(self.path, "scenarios.lst"), "w") as fobj:
fobj.writelines(self.scenlist)
@@ -599,7 +444,125 @@ Snapshot=openclonk-snapshot-(.*)-(.*)-{}-{}-."""
with open(os.path.join(self.path, "scenarios_league.lst"), "w") as fobj:
fobj.writelines(self.league_scenlist)
- self.log.debug("Scenario lists saved.")
+ print("Scenario lists saved.")
self.setTopic("Kein laufendes Spiel.")
- logging.shutdown()
self.shutdowned = True
+
+ def on(self, e):
+ def process(f):
+ signal(e).connect(f)
+ return f
+ return process
+
+# Usage: ./pycrctrl.py path config
+server = PyCRCtrl(sys.argv[1], sys.argv[2])
+
+@server.irc.on("message")
+def on_message(message, user, target, text):
+ channel = server.config["Channels"]["Ingame"]
+ if target == channel and server.ingamechat == "aktiviert" and user.nick != server.config["IRC"]["Nick"]:
+ server.writeToServer(f"[IRC]<{user.nick}> {text}")
+
+@server.irc.on("addressed")
+def on_addressed(message, user, target, text):
+ if target == server.config["Channels"]["Ingame"] and server.ingamechat == "aktiviert":
+ return
+
+ def reply(text):
+ server.irc.say(target, text)
+
+ try:
+ server.checkForCommands(f"<{user}> {server.config['General']['Prefix']}{text}", reply=reply)
+ except Exception as e:
+ traceback.print_exc()
+
+@server.on("cmd-start")
+def start(args):
+ server.stopped = False
+ try:
+ time = int(args[1])
+ except Exception:
+ time = 5
+ args[0](f"/start {time}")
+
+@server.on("cmd-stop")
+def stop(args):
+ def stopping():
+ while server.clonk and server.stopped:
+ server.writeToServer("/start 60000")
+ sleep(100)
+
+ if server.stopped == False:
+ server.stopped = True
+ threading.Thread(target=stopping).start()
+
+@server.on("cmd-queue")
+def queue(args):
+ reply = args[0]
+ reply("Warteschlange:")
+ for i, scen in enumerate(server.queue.queue, start=1):
+ reply(f"{i}. {scen}")
+
+@server.on("cmd-list")
+def lst(args):
+ reply = args[0]
+ if reply != server.writeToServer:
+ reply("Die Szenarienliste kann nur ingame angesehen werden!")
+ return
+
+ reply("Verfügbare Szenarien:")
+ for scen in server.scenlist:
+ reply(scen)
+
+@server.on("cmd-irc")
+def cmd_irc(args):
+ reply = args[0]
+ if not args:
+ reply("Keine Parameter angegeben!")
+
+ if args[1] == "ingamechat":
+ if len(args) <= 1 or args[2] == "off":
+ server.ingamechat = "deaktivert"
+ elif args[2] == "on":
+ server.ingamechat = "aktiviert"
+
+@server.on("cmd-host")
+def host(args):
+ reply = args[0]
+ if len(args) <= 1:
+ reply("Bitte gib einen Szenarionamen an!")
+ scenario = args[1].strip()
+
+ if scenario == "random":
+ scenario = random.choice(server.scenlist).strip()
+
+ elif scenario not in server.scenlist:
+ reply(f"Szenario {scenario} nicht gefunden!")
+
+ if not server.server_thread:
+ server.scenario = scenario
+ server.server_thread = threading.Thread(target=server.startClonk)
+ server.server_thread.start()
+ reply(f"Szenario {scenario} wird jetzt gehostet.")
+
+ elif not server.queue.full():
+ server.queue.put(scenario)
+ reply(f"Szenario {scenario} wurde der Warteschlange hinzugefügt.")
+
+ else:
+ reply("Warteschlange ist voll!")
+
+@server.on("cmd-quit")
+def quit(args):
+ reply = args[0]
+ if server.clonk and server.clonk.stdin:
+ server.clonk.stdin.close()
+ server.server_thread.terminate()
+ server.shutdown()
+ sleep(5)
+ raise SystemExit
+
+try:
+ asyncio.get_event_loop().run_forever()
+except KeyboardInterrupt:
+ sys.exit(0)