Advertisement
kxphotographer

msvutil.py

Jan 26th, 2015
193
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.78 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from datetime import datetime
  5. import os.path
  6. import re
  7. from subprocess import Popen, PIPE
  8. from threading import Thread
  9.  
  10.  
  11. class RequireEulaAgreement(Exception):
  12.     pass
  13.  
  14.  
  15. class ServerStatusException(Exception):
  16.     pass
  17.  
  18.  
  19. class MinecraftServer:
  20.     def __init__(self, jarPath, minHeap='512M', maxHeap='1G', threads=2):
  21.         self.version = None
  22.         self.bootstrap = False
  23.         self.startuptime = None
  24.         self.running = False
  25.         self.shutdown = False
  26.         self.log = []
  27.         self.nowplaying = []
  28.         self.port = None
  29.         self.__terminate = False
  30.         self.process = None
  31.         self.threads = {}
  32.         self.jarPath = os.path.abspath(jarPath)
  33.         self.minHeap = minHeap
  34.         self.maxHeap = maxHeap
  35.         self.javaThreads = threads
  36.  
  37.         if not os.path.exists(jarPath):
  38.             raise IOError('No such file.')
  39.  
  40.     def __del__(self):
  41.         if self.running:
  42.             self.stop()
  43.  
  44.     def __stdOutPollThread(self):
  45.         while not self.__terminate:
  46.             l = self.process.stdout.readline()
  47.             pl = self.__logParser(l)
  48.             self.log.append(pl)
  49.  
  50.             if re.match(r'Starting minecraft server version', pl['content']) is not None:
  51.                 self.bootstrap = True
  52.                 self.version = re.sub(
  53.                     r'Starting minecraft server version ([0-9]+\.[0-9]+\.[0-9]+)',
  54.                     r'\1', pl['content'])
  55.  
  56.             elif re.match(r'You need to agree to the EULA in order to run the server', pl['content']) is not None:
  57.                 self.stop()
  58.                 raise RequireEulaAgreement('EULA Agreement is required.')
  59.  
  60.             elif re.match(r'Starting Minecraft server on .*:.*', pl['content']) is not None:
  61.                 self.port = int(re.sub(r'Starting Minecraft server on .*:([0-9]+)', r'\1', pl['content']))
  62.  
  63.             elif re.match(r'Done \(([0-9]|\.)+s\)!', pl['content']) is not None:
  64.                 self.bootstrap = False
  65.                 self.running = True
  66.                 self.startuptime = float(re.sub(r'Done \((([0-9]|\.)+)s\)!.*', r'\1', pl['content']))
  67.  
  68.             elif re.match(r'.+ joined the game', pl['content']) is not None:
  69.                 name = re.sub(r'(.+) joined the game', r'\1', pl['content']).strip()
  70.                 self.nowplaying.append(name)
  71.  
  72.             elif re.match(r'.+ left the game', pl['content']) is not None:
  73.                 name = re.sub(r'(.+) left the game', r'\1', pl['content']).strip()
  74.                 self.nowplaying.remove(name)
  75.  
  76.             elif re.match(r'Stopping (the |)server', pl['content']) is not None:
  77.                 self.running = False
  78.                 self.shutdown = True
  79.  
  80.             elif re.match(r'FAILED TO BIND TO PORT!', pl['content']) is not None:
  81.                 self.stop()
  82.                 raise Exception('Failed to bind port. Address already in use.')
  83.  
  84.             if len(self.log) >= 2:
  85.                 parsedPrevLog = self.log[-2]
  86.                 if re.match(r'There are [0-9]+/[0-9]+ players online:', parsedPrevLog['content']) is not None:
  87.                     if pl['content'] is not '':
  88.                         self.nowplaying = pl['content'].split(',')
  89.                     else:
  90.                         self.nowplaying = []
  91.  
  92.             if self.process.returncode is not None:
  93.                 self.__terminate = True
  94.  
  95.     def __logParser(self, logline):
  96.         l = logline.strip()
  97.         reorig = re.compile(r'^\[([0-9]+:[0-9]+:[0-9]+)\] \[(.+)\]:(.*)$')
  98.         logDateTime = datetime.strptime(
  99.             datetime.now().strftime('%Y-%m-%d ') + re.sub(reorig, r'\1', l),
  100.             '%Y-%m-%d %H:%M:%S'
  101.         ) if re.match(r'\[([0-9]+:[0-9]+:[0-9]+)\]', l) is not None else None
  102.         logClass = re.sub(reorig, r'\2', l) if re.match(r'\[([0-9]+:[0-9]+:[0-9]+)\] \[(.+)\]:', l) is not None else ''
  103.         logContent = re.sub(reorig, r'\3', l).strip() if re.match(r'\[([0-9]+:[0-9]+:[0-9]+)\]', l) is not None else ''
  104.         return {
  105.             'datetime': logDateTime,
  106.             'class': logClass,
  107.             'content': logContent
  108.         }
  109.  
  110.     def start(self):
  111.         self.process = Popen([
  112.             'java', '-Dfile.encoding=UTF-8'
  113.             '-server',
  114.             '-XX:+UseConcMarkSweepGC',
  115.             '-XX:+UseParNewGC',
  116.             '-XX:+CMSIncrementalPacing',
  117.             '-XX:ParallelGCThreads=%d' % self.javaThreads,
  118.             '-XX:+AggressiveOpts',
  119.             '-Xms%s' % self.minHeap, '-Xmx%s' % self.maxHeap,
  120.             '-jar', self.jarPath, 'nogui'
  121.         ], shell=False, stdin=PIPE, stdout=PIPE, cwd=os.path.dirname(self.jarPath))
  122.  
  123.         self.threads['mspollthread'] = Thread(target=self.__stdOutPollThread)
  124.  
  125.         for key in self.threads.keys():
  126.             self.threads[key].daemon = True
  127.             self.threads[key].start()
  128.  
  129.     def stop(self):
  130.         if self.running:
  131.             self.process.stdin.write('stop\n')
  132.             self.process.wait()
  133.             self.__terminate = True
  134.             for key in self.threads.keys():
  135.                 self.threads[key].join()
  136.             self.shutdown = False
  137.             self.__terminate = False
  138.             self.startupTime = None
  139.             self.process = None
  140.             self.threads = []
  141.         else:
  142.             if self.bootstrap:
  143.                 raise Exception('Server is now starting up.')
  144.             elif self.shutdown:
  145.                 raise Exception('Server is now shutting down.')
  146.             else:
  147.                 raise Exception('Server is not running.')
  148.  
  149.     def restart(self):
  150.         self.stop()
  151.         self.start()
  152.  
  153. if __name__ == '__main__':
  154.     import sys
  155.     s = MinecraftServer(sys.argv[1])
  156.     try:
  157.         s.start()
  158.         s.process.wait()
  159.     except KeyboardInterrupt, e:
  160.         s.stop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement