Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python can't communicate with subprocess of a Minecraft server

Tags:

I'm trying to write a handler/controller for the Minecraft server. My problem is that I can't seem get writing and reading to work properly. When a client issues a command that uses the server class's method serverCom, the Minecraft server's text/log starts to come into the Python window/Python console and the connected client hangs. Also, it seems that after I use Popen, the Minecraft server doesn't really launch until I do write to the server (aka serverCom method). In case anyone is wondering, the Popen goes to a batch file that opens the .jar file. This is on 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()
like image 449
MiB Avatar asked Jan 31 '11 05:01

MiB


1 Answers

I have no idea how a Minecraft server works, but there are a number of problems with your code:

  • You are redirecting stderr to stdout from the created Java process, then expecting a line response from the server. This could be the reason that the Minecraft server is not starting, since it would block on a stderr write (depending on how Windows XP handles it). Additionally, any stderr write (e.g. log write) will destroy any structured responses you may be waiting for.

  • You are reading with sock.recv(N) and then assuming that you get the whole chunk (e.g. password). This is not how TCP works, you may very well get just one character back (especially true if the user types the password interactively e.g. in a Telnet prompt).

  • You are flushing the stdout of the subprocess, which is your input stream. You probably want to flush the stdin of the subprocess. Flushing an input stream makes no sense, it is the output stream that determines when to flush.

like image 121
Krumelur Avatar answered Sep 29 '22 09:09

Krumelur