Skip to content

Instantly share code, notes, and snippets.

@geoffp
Last active November 18, 2023 12:20
Show Gist options
  • Save geoffp/3e2b5e22acf0a4becd8edc18e1caf546 to your computer and use it in GitHub Desktop.
Save geoffp/3e2b5e22acf0a4becd8edc18e1caf546 to your computer and use it in GitHub Desktop.
A simple launcher to allow you to launch minecraft with a specified displayName so a family can play on the same server with one account. Works on Linux, probably Mac OS too. Change the constants to suit your needs.
#!/usr/bin/python
import json, sys, os, subprocess, time, signal, sys
#
# Constants
#
# First and only argument is the player's display name
playerName = sys.argv[1] if len(sys.argv) > 1 else None
if sys.platform == 'darwin':
jsonPath = os.path.expanduser(os.path.join('~', 'Library', 'Application Support', 'Minecraft', 'launcher_profiles.json'))
minecraftPath = "/Applications/Minecraft.app"
else:
jsonPath = os.path.expanduser(os.path.join('~', '.minecraft', 'launcher_profiles.json'))
minecraftPath = os.path.expanduser(os.path.join('~', 'bin', 'Minecraft.jar'))
#
# Free Functions
#
def lockFile():
os.chmod(jsonPath, 0o444)
def unlockFile():
os.chmod(jsonPath, 0o644)
def getDisplayNameParent(jsonData):
auths = jsonData['authenticationDatabase']
# We assume that there's only only entry in the authenticationDatabase
# dictionary, so this should loop only once and work fine.
for authId in auths:
if 'profiles' in auths[authId]:
# same here. it's a loop, but just a lazy way to get the first and
# only dict entry
for profileId in auths[authId]['profiles']:
return auths[authId]['profiles'][profileId]
else:
return auths[authId]
# If we get here, we found nothing!
return None
def getUserName():
with open(jsonPath) as jsonFile:
data = json.load(jsonFile)
displayNameParent = getDisplayNameParent(data)
if displayNameParent != None:
return displayNameParent['displayName']
return None
def changeUserName():
with open(jsonPath) as jsonFile:
data = json.load(jsonFile)
displayNameParent = getDisplayNameParent(data)
if displayNameParent != None:
displayNameParent['displayName'] = playerName
with open(jsonPath, 'w') as jsonFile:
json.dump(data, jsonFile, indent=2)
def killLauncher(launcher):
if sys.platform == 'darwin':
subprocess.call(['killall', 'launcher'])
else:
launcher.kill()
def startLauncher():
if sys.platform == 'darwin':
execPath = ['open', '-W', minecraftPath]
else:
execPath = ['java', '-jar', minecraftPath]
# launch it!
launcher = subprocess.Popen(execPath)
# the callback for handling a ctrl-c
def signal_handler(signal, frame):
print('You pressed Ctrl+C! Killing the launcher...')
killLauncher(launcher);
sys.exit(0)
# register callback for handling ctrl-c
signal.signal(signal.SIGINT, signal_handler)
# return the launcher
return launcher
#
# Main procedure: if a player name was given, alter the file
# accordingly. Otherwise, just unlock the file so a login is
# possible. Either way, launch Minecraft.
#
if (playerName):
unlockFile()
changeUserName()
# lockFile()
else:
unlockFile()
# OK, let's try it...
# New idea: loop and sleep, checking the username every 2 seconds or so
launcher = startLauncher()
print "Watching for an auth refresh..."
while launcher.poll() == None:
# wait for a bit...
time.sleep(1)
currentUserName = getUserName()
# if the user name is None, there's no authentication at
# all. Gotte get one before we can proceed.
if currentUserName == None:
print "Waiting for login..."
continue
# if the user name has switched back after an auth token refresh, let's try this again...
if currentUserName != playerName:
print "Hey, it looks like the display name was reset by an auth refresh, hang on, I need to restart the launcher... "
killLauncher(launcher)
changeUserName()
launcher = startLauncher()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment