Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- from datetime import datetime
- import os.path
- import re
- from subprocess import Popen, PIPE
- from threading import Thread
- class RequireEulaAgreement(Exception):
- pass
- class ServerStatusException(Exception):
- pass
- class MinecraftServer:
- def __init__(self, jarPath, minHeap='512M', maxHeap='1G', threads=2):
- self.version = None
- self.bootstrap = False
- self.startuptime = None
- self.running = False
- self.shutdown = False
- self.log = []
- self.nowplaying = []
- self.port = None
- self.__terminate = False
- self.process = None
- self.threads = {}
- self.jarPath = os.path.abspath(jarPath)
- self.minHeap = minHeap
- self.maxHeap = maxHeap
- self.javaThreads = threads
- if not os.path.exists(jarPath):
- raise IOError('No such file.')
- def __del__(self):
- if self.running:
- self.stop()
- def __stdOutPollThread(self):
- while not self.__terminate:
- l = self.process.stdout.readline()
- pl = self.__logParser(l)
- self.log.append(pl)
- if re.match(r'Starting minecraft server version', pl['content']) is not None:
- self.bootstrap = True
- self.version = re.sub(
- r'Starting minecraft server version ([0-9]+\.[0-9]+\.[0-9]+)',
- r'\1', pl['content'])
- elif re.match(r'You need to agree to the EULA in order to run the server', pl['content']) is not None:
- self.stop()
- raise RequireEulaAgreement('EULA Agreement is required.')
- elif re.match(r'Starting Minecraft server on .*:.*', pl['content']) is not None:
- self.port = int(re.sub(r'Starting Minecraft server on .*:([0-9]+)', r'\1', pl['content']))
- elif re.match(r'Done \(([0-9]|\.)+s\)!', pl['content']) is not None:
- self.bootstrap = False
- self.running = True
- self.startuptime = float(re.sub(r'Done \((([0-9]|\.)+)s\)!.*', r'\1', pl['content']))
- elif re.match(r'.+ joined the game', pl['content']) is not None:
- name = re.sub(r'(.+) joined the game', r'\1', pl['content']).strip()
- self.nowplaying.append(name)
- elif re.match(r'.+ left the game', pl['content']) is not None:
- name = re.sub(r'(.+) left the game', r'\1', pl['content']).strip()
- self.nowplaying.remove(name)
- elif re.match(r'Stopping (the |)server', pl['content']) is not None:
- self.running = False
- self.shutdown = True
- elif re.match(r'FAILED TO BIND TO PORT!', pl['content']) is not None:
- self.stop()
- raise Exception('Failed to bind port. Address already in use.')
- if len(self.log) >= 2:
- parsedPrevLog = self.log[-2]
- if re.match(r'There are [0-9]+/[0-9]+ players online:', parsedPrevLog['content']) is not None:
- if pl['content'] is not '':
- self.nowplaying = pl['content'].split(',')
- else:
- self.nowplaying = []
- if self.process.returncode is not None:
- self.__terminate = True
- def __logParser(self, logline):
- l = logline.strip()
- reorig = re.compile(r'^\[([0-9]+:[0-9]+:[0-9]+)\] \[(.+)\]:(.*)$')
- logDateTime = datetime.strptime(
- datetime.now().strftime('%Y-%m-%d ') + re.sub(reorig, r'\1', l),
- '%Y-%m-%d %H:%M:%S'
- ) if re.match(r'\[([0-9]+:[0-9]+:[0-9]+)\]', l) is not None else None
- logClass = re.sub(reorig, r'\2', l) if re.match(r'\[([0-9]+:[0-9]+:[0-9]+)\] \[(.+)\]:', l) is not None else ''
- logContent = re.sub(reorig, r'\3', l).strip() if re.match(r'\[([0-9]+:[0-9]+:[0-9]+)\]', l) is not None else ''
- return {
- 'datetime': logDateTime,
- 'class': logClass,
- 'content': logContent
- }
- def start(self):
- self.process = Popen([
- 'java', '-Dfile.encoding=UTF-8'
- '-server',
- '-XX:+UseConcMarkSweepGC',
- '-XX:+UseParNewGC',
- '-XX:+CMSIncrementalPacing',
- '-XX:ParallelGCThreads=%d' % self.javaThreads,
- '-XX:+AggressiveOpts',
- '-Xms%s' % self.minHeap, '-Xmx%s' % self.maxHeap,
- '-jar', self.jarPath, 'nogui'
- ], shell=False, stdin=PIPE, stdout=PIPE, cwd=os.path.dirname(self.jarPath))
- self.threads['mspollthread'] = Thread(target=self.__stdOutPollThread)
- for key in self.threads.keys():
- self.threads[key].daemon = True
- self.threads[key].start()
- def stop(self):
- if self.running:
- self.process.stdin.write('stop\n')
- self.process.wait()
- self.__terminate = True
- for key in self.threads.keys():
- self.threads[key].join()
- self.shutdown = False
- self.__terminate = False
- self.startupTime = None
- self.process = None
- self.threads = []
- else:
- if self.bootstrap:
- raise Exception('Server is now starting up.')
- elif self.shutdown:
- raise Exception('Server is now shutting down.')
- else:
- raise Exception('Server is not running.')
- def restart(self):
- self.stop()
- self.start()
- if __name__ == '__main__':
- import sys
- s = MinecraftServer(sys.argv[1])
- try:
- s.start()
- s.process.wait()
- except KeyboardInterrupt, e:
- s.stop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement