Controlling a minecraft server with python - python-3.x

I've searched a lot for this and have not yet found a definitive solution. The closest thing I've found is this:
import shutil
from os.path import join
import os
import time
import sys
minecraft_dir = ('server diectory')
world_dir = ('server world driectory')
def server_command(cmd):
os.system('screen -S -X stuff "{}\015"'.format(cmd))
on = "1"
while True:
command=input()
command=command.lower()
if on == "1":
if command==("start"):
os.chdir(minecraft_dir)
os.system('"C:\Program Files\Java\jre1.8.0_111\bin\java.exe" -Xms4G -Xmx4G -jar craftbukkit-1.10.2.jar nogui java')
print("Server started.")
on = "0"
else:
server_command(command)
When I launch this program and type 'start' the CMD flashes up and closes instantly. Instead I want the CMD to stay open with the minecraft sever running from it. I'm not sure why this happens or what the problem is, any help would be greatly appreciated.
p.s. I have edited this to my needs (such as removing a backup script that was unnecessary) but it didn't work before. The original link is: https://github.com/tschuy/minecraft-server-control

os.system will simply run the command then return to your python script with no way to further communicate with it.
On the other hand using subprocess.Popen gives you access to the process while it runs, including writing to it's .stdin which is how you send data to the server:
def server_command(cmd):
process.stdin.write(cmd+"\n") #just write the command to the input stream
process = None
executable = '"C:\Program Files\Java\jre1.8.0_111\bin\java.exe" -Xms4G -Xmx4G -jar craftbukkit-1.10.2.jar nogui java'
while True:
command=input()
command=command.lower()
if process is not None:
if command==("start"):
os.chdir(minecraft_dir)
process = subprocess.Popen(executable, stdin=subprocess.PIPE)
print("Server started.")
else:
server_command(command)
you can also pass stdout=subprocess.PIPE so you can also read it's output and stderr=subprocess.PIPE to read from it's error stream (if any)
As well instead of process.stdin.write(cmd+"\n") you could also use the file optional parameter of the print function, so this:
print(cmd, file=process.stdin)
Will write the data to process.stdin formatted in the same way that print normally does, like ending with newline for you unless passing end= to override it etc.

Both of the above answers do not work in the environment I tried them in.
I think the best way is to use RCON, not sending keys to a window.
RCON is the protocol used by games to run commands.
Many python libraries support Minecraft RCON, and the default server.properties file has an option for RCON.
We will use the python module: MCRON.
Install it. It works for windows, mac, linux.
Type:
pip install mcrcon
Lets configure your server to allow RCON.
In server.properties, find the line 'enable-rcon' and make it look like this:
enable-rcon=true
Restart and stop your server.
Find the line 'rcon.password' and set it to any password you will remember.
You can leave the port default at 25575.
Now, open your terminal and type:
mcron localhost
Or your server ip.
You will be prompted to enter the password you set.
Then you can run commands and will get the result.
But we are doing this with python, not the PYPI MCRON scripts - so do this.
from mcrcon import MCRcon as r
with r('localhost', 'insertyourpasswordhere') as mcr:
resp = mcr.command('/list')
print(resp) #there are 0/20 players online: - This will be different for you.

Related

Python: subprocess + isql on windows: a bytes-like object is required, not 'str'

This is a company issued laptop and I can't install any new software on it. I can install Python modules to my own directory though. I need to run something on Sybase and there are 10+ servers. Manual operation is very time consuming hence I'm looking for the option as Python + subprocess.
I did some research and referred to using subprocess module in python to run isql command. However, my version doesn't work. The error message is TypeError: a bytes-like object is required, not 'str'. This error message popped up from the "communicate" line.
I can see my "isql" has connected successfully as I can get a isql.pid.
Anything I missed here?
Thank you
import subprocess
from subprocess import Popen, PIPE
import keyring
from textwrap import dedent
server = "MYDB"
cmd = r"C:\Sybase15\OCS-15_0\bin\isql.exe"
interface = r"C:\Sybase15\ini\sql.ini"
c = keyring.get_credential("db", None)
username = c.username
password = c.password
isql = Popen([cmd, '-I', interface,
'-S', server,
'-U', username,
'-P', password,
'-w', '99999'], stdin=PIPE, stdout=PIPE)
output = isql.communicate("""\
SET NOCOUNT ON
{}
go
""".format("select count(*) from syslogins"))[0]
From the communicate() documentation,
If streams were opened in text mode, input must be a string. Otherwise, it must be bytes.
By default, streams are opened in binary format, but you can change it to text mode by using the text=True argument in your Popen() call.

Pythonw, pyw, & arg, /B won't make python's http-server run in background

I've tried every method in the title to run it in the background but here are the issues i got trying to use them:
pythonw and pyw: server doesn't work, going to localhost:8000 error with ERR_EMPTY_RESPONSE.
& arg and START/B: doesn't start script in background and instead output server log
so now I'm short on ideas on how to run this script in the background.
Using pythonw, it should help to explicitly redirect stdout and stderr to a file – maybe this behavior is somehow related to the problem described here (although this seems to be specific to Python 2.7). Not capturing the output by redirecting it to os.devnull seems to work as well.
The following script produces a minimum working server example with pythonw for me (using Python 3.7.9):
import http.server
import os
import sys
if __name__ == "__main__":
sys.stdout = sys.stderr = open(os.devnull, "w")
httpd = http.server.HTTPServer(("localhost", 8000), http.server.SimpleHTTPRequestHandler)
httpd.serve_forever()

Why does f.write() in a Python file on a server not work?

I have a Python app that works locally, but on the server something goes wrong. So I have to do some debugging in an SSH session. The server logs tell me that something goes wrong in utils/do_something.py. I have created utils/log (rights 777), where the debugging values are supposed to go.
do_something.py looks like this:
import os
parent_folder = os.path.dirname(__file__)
log_file = os.path.join(parent_folder, 'log')
def do_something(arg1, arg2):
print('XXX')
f = open(log_file, 'a')
f.write('XXX\n')
stuff = goes.wrong
Loading the page causes do_something to run (as confirmed by the error logs). Locally the expected XXX appears in the console and in the log file. But nothing happens on the server.
I created a second file utils/blah.py:
print('BLAH')
import os
parent_folder = os.path.dirname(__file__)
log_file = os.path.join(parent_folder, 'log')
f = open(log_file, 'a')
f.write('BLAH\n')
When I run it with python blah.py, the expected BLAH appears in the console and the logfile.
I don't care that much about the difference between local and production server.
But I would like to understand the difference between do_something.py and blah.py.
Is there a better way to debug in an SSH session?
I work in a virtualenv in a mod_wsgi 4.6.5/Python3.7 environment on a Webfaction server. Some details about it can be seen in this question on the Webfaction forum.
Edit 1: On the server print seems to be discouraged anyway.
(See Where do things go when I ‘print’ them from my Django app?)
But what matters to me is f.write(). I just added print for comparison.
Edit 2: It is the same when I use the logging module. It works when I run blah.py, but nothing happens when loading the page runs do_something.
Edit 3: I tried the same with a simpler app, and the result is the same.I added the logging in views.py:
from django.http import HttpResponse
import os
parent_folder = os.path.dirname(__file__)
log_file = os.path.join(parent_folder, 'log')
def home_view(request):
f = open(log_file, 'a')
f.write('XXX\n')
return HttpResponse("Hello from home view.")
Locally this writes XXX to the log file every time the page is loaded. But not on the server.There are no errors in the server log.
Using the logging module: I am not sure why that did not work, but now it does.
import os
import logging
parent_folder = os.path.dirname(__file__)
log_file = os.path.join(parent_folder, 'log')
logging.basicConfig(filename=log_file, level=logging.DEBUG)
logging.debug('This works.')
Possibly I have used it with filename='log' instead of filename=log_file.
Locally the former creates the log file in the root folder. But on the server it must already exist.
Writing rights: It seems worth mentioning that touch log gave me a file I could not wright to, and lacking sudo I could not use chmod. So I used the trick install -b -m 777 /dev/null log.
It appears that you never explicitly close the file after writing, so the output is probably buffered. Your server and your local machine may have different settings with respect to file buffering, which would explain the differences you're experiencing.
If you want to open, write, and close a file, python's context managers are the best way to do it:
def home_view(request):
with open(log_file, 'a') as f:
f.write('XXX\n')
return HttpResponse("Hello from home view.")
You might check the privileges on the utils folder, and on any parent folders. Many times an http server will be running as user 'nobody', which has practically zip privileges. So unless the utils folder itself also has 0777 privileges, that could create a problem. You also might want to put the f.write in a try ... except block so that you can catch the error specifically and create a more useful / informative message about the error. Best.

python subprocess call from wsgi

When I run the exact same function from the python3 interpreter vs, apache via mod wsgi, they both run error free but one returns the command text from apache the stout is simply always blank. Again I am running the exact same function.
Backgorund, I want to run svn update on some code to do so I am using subprocess to simply call "svn update /path/to/repo"
def update():
p1=subprocess.Popen(["svn", "update", "/var/www/myrepocode"],stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
(ret, stderr) = p1.communicate(timeout=10)
return (str(ret.decode('utf-8')))
when i run from the python3 shell or another python 3 script called from the shell
from test import update
print(update())
it works fine and i get
"Updating '/var/www/myrepocode':\nAt revision 27.\n"
when I have a wsgi script and execute it from accessing the web page
def application(environ, start_response):
...
output = output + "---"+update() +"---"
...
response_headers = [('Content-type', 'text/html'), ('Content-Length',
str(len(output)))]
start_response(status, response_headers)
return [output.encode('utf-8')]
I get an empty string:
------
I do not have a python virtual environment, just normal python3 and there are no errors thrown either. I suspect this has to do with the user running it but I really don't know. setting shell=True doesn't change anything either. Any help is greatly appreciated.
Your update method does not return nor print stderr . Maybe you should check for errors first by adding the following line in the update method.
print(stderr)
If errors are produced, can you post them?

Unable to run python cgi scripts using CGIHTTPRequestHandler in Python 3.3

I am a noob; trying to create and use a simple webserver in Python that executes CGI scripts written in Python. I am using Windows XP and Python v3.3.0. I have a "myserver" directory which contains "myserver.py","sample.html" and the directory "cgi-bin" which in turn contains "cgi_demo.py"
myserver.py
from http.server import HTTPServer
from http.server import CGIHTTPRequestHandler
port = 8080
host = '127.0.0.1'
server_address = (host,port)
httpd = HTTPServer(server_address,CGIHTTPRequestHandler)
print("Starting my web server on port "+str(port))
httpd.serve_forever()
cgi_demo.py
import cgi
import cgitb; cgitb.enable()
print("Content-type: text/html")
print
print("<html><body>")
for i in range(0,100):
print(i,"<br>")
print("</body></html>")
Now the directory listing works fine for "myserver" but not for "cgi-bin"; maybe that is how it is coded - I don't have a problem here. "sample.html" is retrieved fine too. However, the execution of "cgi_demo.py" is not proper. I get a blank page in the browser; and the console window (which is blank too) appears and disappears. Moreover, on the server's console I get the message
127.0.0.1 - - [29/Nov/2012 12:00:31] "GET /cgi-bin/cgi_demo.py HTTP/1.1" 200 -
127.0.0.1 - - [29/Nov/2012 12:00:31] command: C:\Python33\python.exe -u "D:\python apps\my web server\cgi-bin\cgi_demo.py" ""
127.0.0.1 - - [29/Nov/2012 12:00:32] CGI script exited OK
Please tell me what is wrong! I get the feeling that the output stream of my script is not connected to the server. What am I doing wrong? Don't say that I have to extend CGIHTTPRequestHandler!!
SORRY for the trouble!
Well, it is my fault. 2 things to note:
[1]The console window that appeared and disappeared; it only happens when I use IDLE to execute the server. If the script is already running in a normal windows console then this does not happen. My Feeling was WRONG.
[2]There is an bug/error in my cgi script. After printing the HTTP header; the print statement that I wrote was just "print" instead of actually being "print()".This is so embarrassing! But, even then why didn't the interpreter catch this error?

Resources