我正在尝试为 Minecraft 服务器编写一个处理程序/控制器。我的问题是我似乎无法正常写作和阅读。当客户端发出使用服务器类方法
serverCom
的命令时,Minecraft 服务器的文本/日志开始进入 Python 窗口/Python 控制台,并且连接的客户端挂起。另外,似乎在我使用 Popen
后,Minecraft 服务器直到我写入服务器(又名 serverCom
方法)后才真正启动。如果有人想知道,Popen
会转到打开 .jar 文件的批处理文件。这是在 Windows XP 上。
import subprocess
import os
import configobj
import socket
import threading
from time import sleep
config = configobj.ConfigObj("config.ini")
cHost = config["hostip"]
cPort = int(config["hostport"])
cBuffer = int(config["serverbuffer"])
cClients = int(config["numberofclients"])
cPassword = config["password"]
class server(object):
def __init__(self):
self.process = False
self.folder = "C:\\servers\\minecraft-danny"
self.max = configobj.ConfigObj("%s\\simpleserver.properties"%self.folder)["maxPlayers"]
def serverStart(self):
if not self.process:
self.process = subprocess.Popen("java -Xmx1024m -Xms1024m -jar minecraft_server.jar nogui", cBuffer, None, subprocess.PIPE, subprocess.PIPE, subprocess.STDOUT, cwd = self.folder)
return True
return False
def serverStop(self):
if self.process:
self.serverCom("stop")
self.process = False
return True
return False
def serverCom(self, text):
if self.process:
self.process.stdout.seek(2)
self.process.stdin.write("%s\n"%text)
self.process.stdin.flush()
self.process.stdout.flush()
return (str(self.process.stdout.readline()), True)
return ("", False)
def serverPlayers(self):
if self.process:
self.serverCom("list")
x = self.serverCom(" ")[0].split(":")[3].replace("\n","").replace(" ","")
if x == "":
x = 0
else:
x = len(x.split(","))
return (x, self.max)
return (0,self.max)
serv = server()
def client(cnct, adr):
global count
try:
dat = str(cnct.recv(cBuffer)).split(" ")
ans = False
if dat[0] == "start":
print "Client %s:%s started the MC Server....."%(adr[0], adr[1])
x = serv.serverStart()
sleep(1)
serv.serverCom(" ")
serv.serverCom(" ")
sleep(5)
if x:
ans = "Server is now online."
else:
ans = "Server is already online."
elif dat[0] == "stop":
print "Client %s:%s stopped the MC Server....."%(adr[0], adr[1])
x = serv.serverStop()
sleep(6)
if x:
ans = "Server is now offline."
else:
ans = "Server is already offline."
elif dat[0] == "commun":
print "Client %s:%s executed a command on the MC Server....."%(adr[0], adr[1])
serv.serverCom(" ".join(dat[1:]))
x = serv.serverCom(" ")
if x[1]:
ans = x[0]
else:
ans = "No return text, server is offline or not responding."
elif dat[0] == "players":
print "Client %s:%s recieved the player count from the MC Server....."%(adr[0], adr[1])
pc = serv.serverPlayers()
ans = "%s/%s"%(pc[0],pc[1])
elif dat[0] == "help":
print "Client %s:%s recieved the help list....."%(adr[0], adr[1])
ans = "__________\nstart - Starts the server.\nstop - Stops the server.\ncommun <command> - Writes to server's console.\nplayers - Returns player count.\nhelp - Shows this help.\nclose - Closes client connections.\n__________"
elif dat[0] == "close":
pass
else:
ans = "Command '%s' is not valid."%dat[0]
if ans:
cnct.send("PASS")
cnct.send("%s\n"%ans)
threading.Thread(target = client, args = (cnct, adr,)).start()
else:
cnct.send("DICN")
cnct.send("Connection to server closed.\n")
cnct.close()
print "Client %s:%s disconnected....."%(adr[0], adr[1])
if count:
count -= 1
except:
cnct.close()
print "Client %s:%s disconnected..... "%(adr[0], adr[1])
if count:
count -= 1
print "-MC Server Control Server v0.0.1 BETA-"
print "Starting up server....."
print "Connecting to socket....."
count = 0
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.bind((cHost, cPort))
sck.listen(5)
print "Connected and listening on %s:%s....."%(cHost, cPort)
print "Setting up client listener, allowing %s clients to connect at a time....."%cClients
while True:
for x in range(cClients):
(cnct, adr) = sck.accept()
print "Client %s:%s connected....."%(adr[0], adr[1])
cnct.send("Welcome to MineCraft Server Control.\n\nPlease enter server control password.\n")
ps = str(cnct.recv(cBuffer))
if count < cClients:
if ps == cPassword:
cnct.send("CRRT")
cnct.send("%s was correct.\nIf you need help type 'help'."%ps)
count += 1
threading.Thread(target = client, args = (cnct, adr,)).start()
else:
cnct.send("WRNG")
cnct.send("%s wasn't the correct password, please try again."%ps)
cnct.close()
print "Client %s:%s rejected....."%(adr[0], adr[1])
else:
cnct.send("WRNG")
cnct.send("Too many clients connected to MineCraft Server Control")
cnct.close()
print "Client %s:%s rejected....."%(adr[0], adr[1])
sck.close()
我不知道 Minecraft 服务器是如何工作的,但您的代码存在很多问题:
您正在将 stderr 从创建的 Java 进程重定向到 stdout,然后期待来自服务器的线路响应。 这可能是 Minecraft 服务器未启动的原因,因为它会阻塞 stderr 写入(取决于 Windows XP 如何处理它)。此外,任何 stderr 写入(例如日志写入)都会破坏您可能正在等待的任何结构化响应。
您正在使用
sock.recv(N)
阅读,然后假设您获得了整个块(例如密码)。这不是 TCP 的工作方式,您很可能只返回一个字符(如果用户以交互方式输入密码,例如在 Telnet 提示符中,尤其如此)。您正在刷新子进程的标准输出,这是您的输入流。您可能想刷新子进程的 stdin 。刷新输入流没有任何意义,输出流决定何时刷新。
Idk,但如果是本地服务器,则可以使用 mcrcon 代替。在这里阅读他们的文档:https://github.com/barneygale/MCRcon