Run a small private local minecraft server and got tired of trying to keep it up to date with all of the snapshots. Made a script that will perform backups, update the server, and gracefully stop the server.
import docker
import os
import shutil
import time
client = docker.from_env()
def getURL(): #Gets URL from User
userInput = input("Please provide the URL for the server jar you wish to update too: ")
if userInput[0:4] != "http": #Validate that the URL is valid
print("Invalid URL. Make sure it includes the http protocall")
userInput = getURL()
if userInput[-4:] != ".jar": #Verify that we are downloading the correct file
print("Invalid URL. Missing proper file extension")
userInput = getURL()
return userInput
def fetchJar(URL): #Downloads the server jar file from a URL
if URL[0:4] != "http" or URL[-4:] != ".jar": #One last check to see if the URL was malformed between input and download
print("Function fetchJar was passed a malformed URL")
print("URL:" + URL)
exit(1)
file = URL.split("/")[-1] #Get the file name of the file we are downloading
if os.path.exists("/tmp/" + file) == True:
os.remove("/tmp/" + file)
if os.path.exists("/tmp/" + file) == True: #Verify that the remove was successful
print("ABORT. os.remove failed. Please delete /tmp/" + file)
exit(127)
print("Fetching Server Jar")
os.system("wget --quiet -O /tmp/" + file + " " + URL)
return file #Returns file name to ensure consistency across the code
def getVersion(): #Get version from User
version = input("What version are you updating too: ")
return version
def renameServer(version, file): #Takes a file and renames it to the server exec standard. "minecraft.VERSION.jar"
newFile = "minecraft-server." + version + ".jar"
if os.path.exists("/tmp/" + newFile) == True:
os.remove("/tmp/" + newFile)
if os.path.exists("/tmp/" + newFile) == True: # Verify the remove was successful
print("ABORT. os.remove failed. Please delete /tmp/" + file)
exit(126)
os.rename("/tmp/" + file, "/tmp/" + newFile)
return newFile #Returns the new file name
def moveServer(file): #Moves server from /tmp to /opt/minecraft-server/
if os.path.exists("/opt/minecraft-server/" + file) == True: #Verify to see if we are replacing a version that already exists EX: Corrupted server jar
if input("Server version already exists. Overwrite? (y/n): ").lower() == "y":
os.remove("/opt/minecraft-server/" + file)
else:
print("Aborting upgrade")
exit(0) #0 exit code since its not a error
if os.path.exists("/opt/minecraft-server/" + file) == True:
print("ABORT. os.remove failed. Please delete /tmp/" + file)
exit(125)
shutil.move("/tmp/" + file, "/opt/minecraft-server/" + file) #Since /tmp usually resides on a ramfs. Need to use shutil.move as we are going cross device and built in functions dont support it well
def dockerStop(version): # Stop and Verify that its stopped
print("Stopping Docker container")
if client.containers.list(filters={"name":"minecraft"}) == []: #Check to see if its already stopped. If so, just return
return
os.system("mcrcon -H 192.168.0.6 -P 25575 -p REDACTED 'say Stopping server in 60 seconds to update to '" + version) #Use mcRcon to connect and issue remote commands
time.sleep(55)
os.system("mcrcon -H 192.168.0.6 -P 25575 -p REDACTED 'say Stopping server in 5 seconds'")
time.sleep(5)
os.system("mcrcon -H 192.168.0.6 -P 25575 -p REDACTED 'kick @a Updating'")
os.system("mcrcon -H 192.168.0.6 -P 25575 -p REDACTED 'save-all flush'")
os.system("mcrcon -H 192.168.0.6 -P 25575 -p REDACTED 'stop'")
print("Waiting for server to gracefully stop")
while client.containers.list(filters={"name":"minecraft"}) != []: #Waits in a loop until the docker container drops off the list
time.sleep(1)
def backupWorld(): #Backs up the save
print("Backing up the world file")
os.chdir("/opt/minecraft-server")
os.system("cp -a world Backups/world-$(date +%B-%d-%Y-%R)") #Runs the backup command and timestamps the backup
def updateServer(file): #Update executable
os.chdir("/opt/minecraft-server") #Make sure we are in the correct directory
os.remove("server.jar") #Remove the old link
os.system("ln -s " + file + " server.jar") #Links the new file to the server.jar exec
def main(): #Main function that ties everything together
URL = getURL()
version = getVersion()
file = fetchJar(URL)
file = renameServer(version, file)
moveServer(file)
dockerStop(version)
backupWorld()
updateServer(file)
print("Starting Minecraft")
os.system("docker start minecraft")
main()