aboutsummaryrefslogtreecommitdiff
path: root/mcwrapper
blob: b3cf2238f12416d5d2fa20706c1335a9b8d0dd3b (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/env python3
import os
import sys
import re
import subprocess
import datetime
import signal
import time
from threading import Thread

__log_prefix__ = '/home/minecraft/log/'
__folderprefix__ = '/dev/shm/'
__folder__ = __folderprefix__
__players__ = 'players'
__status__ = 'status'
__pipe__ = 'input_pipe'
__log__ = 'none.log'

__STATUSSTRINGS__ = { 0: "Not running",
		1: "Starting",
		2: "Running",
		3: "Stopping"
		}

def __setfiles__(identifier):
	global __folder__
	global __players__
	global __status__
	global __pipe__
	global __log__
	__folder__ = __folderprefix__ + 'mcwrapper_' + identifier + '/'
	__players__ = __folder__ + 'players'
	__status__ = __folder__ + 'status'
	__pipe__ = __folder__ + 'input_pipe'
	__log__ = __log_prefix__ + identifier + '/' + \
		datetime.datetime.now().strftime('%y-%m-%d-%H-%M-%S')

def __user_join__(username):
	print("User '" + username + "' joined server.")
	with open(__players__, 'a') as f:
		f.write(username + '\n')

def __user_leave__(username):
	print("User '" + username + "' left server.")
	players = set()
	with open(__players__) as f:
		for line in f:
			pl = line.rstrip()
			if pl != username:
				players.add(line.rstrip())
	with open(__players__, 'w') as f:
		f.writelines(players)
		if players:
			f.write('\n')


def __server_init__(identifier):
	__setfiles__(identifier)
	__server_clean__() # Clean before execute
	print("Wrapper initializing with identifier: " + identifier)
	try:
		os.mkdir(__folder__)
	except Exception:
		pass
	if os.path.isfile(__status__):
		sys.exit("Server status file already exists. Is another wrapper running?")
	with open(__status__, 'w') as f:
		f.write(__STATUSSTRINGS__[1])
	os.mkfifo(__pipe__, 0o640)

def __server_start__():
	print("Server start.")
	with open(__status__, 'w') as f:
		f.write(__STATUSSTRINGS__[2])
	pass

def __server_stop__():
	print("Server stop.")
	with open(__status__, 'w') as f:
		f.write(__STATUSSTRINGS__[3])
	pass

def __server_clean__():
	print("Wrapper clean.")
	try:
		os.remove(__players__)
	except Exception:
		pass
	try:
		os.remove(__status__)
	except Exception:
		pass
	try:
		os.remove(__pipe__)
	except Exception:
		pass

def __parse_line__(line):
	if ': Done' in line:
		__server_start__()
	elif ': Stopping the server' in line:
		__server_stop__()
	elif 'logged in with entity id' in line:
		name = line[len('[00:00:00] [Server thread/INFO]: '):]
		name = name[:name.index('[')]
		__user_join__(name)
	elif 'left the game' in line:
		name = line[len('[00:00:00] [Server thread/INFO]: '):]
		name = name[:name.index(' ')]
		__user_leave__(name)

#################################################################################

class __InputThread__(Thread):
	def __init__(self, pipein, pipeprocess):
		Thread.__init__(self, name='InputThread')
		self.pipein = pipein
		self.pipeprocess = pipeprocess
		self.stopread = False
	def stopexec(self):
		self.stopread = True
	def wake(self):
		with open(__pipe__, 'w') as f:
			f.write("\n")
			f.flush()
	def run(self):
		with open(self.pipein, 'r') as p:
			while not self.stopread:
				ln = p.readline()
				if ln:
					print("Input: " + ln, end="")
					self.pipeprocess.write(bytes(ln, sys.getdefaultencoding()))
					self.pipeprocess.flush()
				else:
					time.sleep(1)
			try:
				os.remove(__pipe__)
			except Exception:
				pass

def __server_send_stop__():
	global prc
	prc.stdin.write(bytes("/stop\n", sys.getdefaultencoding()))
	prc.stdin.flush()

def mcexec(identifier, cmd):
	"""Executes cmd and parses output for server status changes.

	  cmd     - List of program and arguments to be executed.
	  logfile - Path in string. To entered file will be printed out full process output.
	  """
	global prc
	__server_init__(identifier)
	try:
		os.makedirs(__log_prefix__ + identifier)
	except Exception:
		pass
	prc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
			stderr=subprocess.STDOUT)
	inputThread = __InputThread__(__pipe__, prc.stdin)
	inputThread.start()
	inputThread.wake() # Input thread is stack in waiting for first line
	for linen in prc.stdout:
		line = linen.decode(sys.getdefaultencoding())
		with open(__log__, 'a') as flg:
			flg.write(line)
		__parse_line__(line.rstrip())
	inputThread.stopexec()
	__server_clean__()

#################################################################################

def __get_status__(identifier):
	if not os.path.isfile(__status__):
		return 0
	else:
		with open(__status__) as f:
			status = f.readline().rstrip()
			for key, val in __STATUSSTRINGS__.items():
				if val == status:
					return key

def server_stop(identifier):
	__setfiles__(identifier)
	status = __get_status__(identifier)
	if status == 0:
		sys.exit("Such server is not running")
	elif status == 1:
		print("Server is starting. Waiting...")
		while __get_status__(identifier) != 2:
			time.sleep(1)
	elif status == 3:
		sys.exit("Server already stops")
	with open(__pipe__, 'w') as f:
		f.write("/stop\n")
		f.flush()
	while os.path.isfile(__status__):
		pass

def server_say(identifier, message):
	__setfiles__(identifier)
	if __get_status__(identifier) != 2:
		sys.exit("Server is not running")
	with open(__pipe__, 'w') as f:
		f.write("/say " + ' '.join(map(str, message)))
		f.flush()

#################################################################################

def __signal_term__(_signo, _stack_frame):
	__server_send_stop__()

if __name__ == '__main__':
	todo = sys.argv[1]
	identifier = sys.argv[2]
	if todo == 'start':
		signal.signal(signal.SIGTERM, __signal_term__)
		cmd = sys.argv[3:]
		mcexec(identifier, cmd)
		__server_clean__()
	elif todo == 'stop':
		server_stop(identifier)
	elif todo == 'say':
		message = sys.argv[3:]
		server_say(identifier, message)
	else:
		print("unknown action: " + todo)