1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# vim: expandtab ft=python ts=4 sw=4 sts=4:
import os
import sys
import subprocess
import time
from threading import Thread
from .import prints
__INPUTPIPE__ = 'input_pipe'
__PIDFILE__ = 'server.pid'
class MCWrapper:
"Minecraft server wrapper class"
def __init__(self, command):
self.process = None
self.command = command
self._running = False
self._hook_start = []
self._hook_stop = []
self._hook_line = []
prints.info("Server wrapper initializing")
if os.path.isfile(__PIDFILE__):
with open(__PIDFILE__) as file:
lpid = int(file.readline())
try:
os.kill(lpid, 0)
except OSError:
prints.warning("Detected forced termination of previous server"
" wrapper instance.")
else:
prints.error("Another wrapper is running with given "
"identifier.", -1, 1)
try:
os.mkfifo(__INPUTPIPE__, 0o640)
except FileExistsError:
pass
self.inputthread = Thread(target=self.__input_thread__,
daemon=True)
self.outputhread = Thread(target=self.__output_thread__,
daemon=True)
def clean(self):
"Cleans files generated by wrapper"
prints.info("Server wrapper clean.")
try:
os.remove(__INPUTPIPE__)
except FileNotFoundError:
pass
try:
os.remove(__PIDFILE__)
except FileNotFoundError:
pass
def start(self):
"Start Minecraft server"
self.process = subprocess.Popen(
self.command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
start_new_session=False)
for h in self._hook_start:
h()
self._running = True
with open(__PIDFILE__, "w") as file:
file.write(str(self.process.pid))
if not self.inputthread.is_alive():
self.inputthread.start()
if not self.outputhread.is_alive():
self.outputhread.start()
def stop(self):
"Sends /stop command to Minecraft server"
if self.running():
self.process.stdin.write(bytes(
"/stop\n", sys.getdefaultencoding()))
self.process.stdin.flush()
self._running = False
def running(self):
"Returns True if mc server is running. Othervise False."
return True
def write_to_terminal(self, text):
"Write to server terminal. If server not running it does nothing"
if self._running:
prints.info("Input: " + text.rstrip(), 1)
self.process.stdin.write(bytes(text, sys.getdefaultencoding()))
self.process.stdin.flush()
return True
else:
return False
def hook_start(self, handler):
self._hook_start.append(handler)
def hook_stop(self, handler):
self._hook_stop.append(handler)
def hook_line(self, contains, handler):
n = dict()
n["contains"] = contains
n["handler"] = handler
self._hook_line.append(n)
def __parse_line__(self, line):
i = 0
while i < len(self._hook_line):
if self._hook_line[i]["contains"] in line:
self._hook_line[i]["handler"](line)
i += 1
def __output_thread__(self):
for linen in self.process.stdout:
line = linen.decode(sys.getdefaultencoding())
prints.info(line.rstrip(), 2, notime=True)
self.__parse_line__(line.rstrip())
def __input_thread__(self):
with open(__INPUTPIPE__, 'r') as pipe:
while True:
line = pipe.readline().rstrip()
# TODO use polling
if line:
self.write_to_terminal(line + "\n")
else:
time.sleep(3)
|