-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbedrock_server.py
executable file
·224 lines (213 loc) · 7.21 KB
/
bedrock_server.py
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
from subprocess import Popen, PIPE, STDOUT
import threading
import re
import os
import time
import datetime
import json
Magic = "vYK1JQ6DgAUdwORN"
Alias = "becrock_serve"
node=None
logging=None
cwd=os.path.abspath('.')
nLine=0
def _pOut():
global node
with node.proc:
while True:
if node.killio:
break
if node.proc is None:
time.sleep(0.1)
continue
# if node.proc is None or node.proc.returncode or node.proc.poll() is not None:
# continue
o=node.proc.stdout.readline().strip()
if not o:
continue
logging.info("["+node.name+"]:\t"+o)
node.stdout.append(o)
time.sleep(0.1)
def _pIn():
global node
with node.proc:
while True:
if node.killio:
break
if node.proc is None:
time.sleep(0.05)
continue
if not node.ready:
time.sleep(0.05)
continue
for s in node.stdin.copy():
node.proc.stdin.write(s+'\n')
node.proc.stdin.flush()
node.stdin.remove(s)
time.sleep(0.05)
# Starts the minecraft server
def _start():
global node
if node.proc==None:
logging.info("["+node.name+"]:\tStarting Bedrock Server")
node.killio=False
#node.thIO['stdin']=threading.Thread(target=_pIn)
node.thIO['stdout']=threading.Thread(target=_pOut)
node.proc=Popen([node.server_path], stdout=PIPE, stdin=PIPE, stderr=STDOUT, universal_newlines=True,cwd=cwd)
node.stdin=[]
node.stdout=[]
node.users={}
if os.path.isfile('users.json'):
with open('users.json','r') as f:
node.users=json.load(f)
for v in node.users.keys():
node.users[v]['is_online']=False
node.watchers={'user_watch':[0,0]}
time.sleep(2)
# node.thIO['stdin'].start()
node.thIO['stdout'].start()
version=_watch('\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} INFO\] Version (.*)')
_watch('\[INFO\] Server started\.')
if node.killio:
logging.info("["+node.name+"]:\tBedrock Server Crashed")
return False
node.ready=True
logging.info("["+node.name+"]:\tBedrock Server Started, Version: %s",version)
return True
return False
# Sends a command to stdin
def _cmd(c,a=None,rt=0):
global node
# await=regexp response
node.stdin.append(c)
for s in node.stdin.copy():
node.proc.stdin.write(s+'\n')
node.proc.stdin.flush()
node.stdin.remove(s)
if a==None:
return
return _watch(a,rt)
# Awaits stdout to match regex pattern
def _watch(r,t=0):
global nLine
global node
while True:
if node.killio:
_stop(force=True)
break
stdout=node.stdout.copy()
if nLine<len(stdout):
for l in stdout[nLine:]:
if l=="Crash":
_stop(force=True)
break
if t==0:
m=re.search(r,l)
if m:
return True
elif t==1:
m=re.match(r,l)
if m:
return m
elif t==2:
return re.findall(r,l)
nLine=len(stdout)
time.sleep(1)
# Requests server shutdown
def _stop(force=False):
if not node.ready and not force:
return
logging.info("["+node.name+"]:\tStopping Bedrock Server")
if node.proc!=None:
if not force:
_cmd('stop','Quit correctly')
node.proc.wait(timeout=15)
node.proc.terminate()
node.proc=None
logging.info("["+node.name+"]:\tBedrock Server Stopped")
logging.info("["+node.name+"]:\tStopping Bedrock Server IO")
node.killio=True
# if node.thIO['stdin'].is_alive():
# node.thIO['stdin'].join()
# if node.thIO['stdout'].is_alive():
node.thIO['stdout'].join()
node.ready=False
node.stdin=None
logging.info("["+node.name+"]:\tStopped Bedrock Server IO")
if not node.preserveLog:
node.stdout=None
# Called before _loop_
def __init__(n,l):
global node
global logging
global nLine
node=n
logging=l
node.id=Magic
# node.preserveLog=False
node.proc=None
node.thIO={'stdin':None,'stdout':None}
node.stdin=[]
node.stdout=[]
node.ready=False
node.start=_start
node.stop=_stop
node.cmd=_cmd
node.watch=_watch
node.preserveLog=False
node.killio=False
node.users={}
node.watchers={'user_watch':[0,0]}
node.server_path=cwd+os.path.sep+'bedrock_server'
_start()
logging.info("["+node.name+"]:\tInitialized")
# Called before node is reloaded after modification
def __reinit__(self):
if node.proc!=None:
_stop()
# Called before node is terminated
def __deinit__():
if node.proc!=None:
_stop()
# Called at intervals default is 0.1s
def __loop__(self):
global node
if time.time()-node.watchers['user_watch'][0]>=1:
node.watchers['user_watch'][0]=time.time()
if not node.ready:
return
if node.stdout is None:
return
stdout=node.stdout.copy()
oLine=node.watchers['user_watch'][1]
if oLine<len(stdout):
for l in stdout[oLine:]:
m=re.match('\[INFO\] Player disconnected: ([^,]*), xuid: (.+)$',l)
if m is not None:
m=list(m.groups())
td=datetime.datetime.now().astimezone().isoformat()
if not m[1] in node.users:
continue
node.users[m[1]]['is_online']=False
with open('users.json','w') as f:
json.dump(node.users,f)
continue
m=re.match('\[INFO\] Player connected: ([^,]*), xuid: (.+)$',l)
if m is not None:
m=list(m.groups())
print(m)
td=datetime.datetime.now().astimezone().isoformat()
logging.info("["+node.name+"]:\t%s"%({'username':m[0],'xuid':m[1],'last_seen':None,'last_login': None,'address':None,'is_online':True}))
if not m[1] in node.users:
node.users[m[1]]={'username':m[0],'xuid':m[1],'last_seen':None,'last_login': None,'address':None,'is_online':True}
node.users[m[1]]['last_seen']=td
node.users[m[1]]['first_login']=td
node.users[m[1]]['is_online']=True
with open('users.json','w') as f:
json.dump(node.users,f)
continue
oLine=len(stdout)
node.watchers['user_watch'][1]=oLine
time.sleep(1)
#
pass