This document describes the PortableMC API usable via importing the portablemc module. This file will
not enter in details of classes or functions signatures, but will describe the concept of them and for
what they are used. Many classes and method provides docstring that describe signatures.
Examples in this documentation are using linux paths and can reference variables defined in previous examples.
A context in PMC is used to keep track of core directories used to install and run Minecraft. It also provides methods to list installed game versions or test if a specific version is existing. In current version, a context stores paths to the following directories:
work_dir, where the game actually run, where it stores saves, screenshots and user-specific thing.versions_dir, versions' directories are stored with metadata and JAR file.assets_dir, game assets' directory, with indexes, object, skins...libraries_dir, where game libraries are stored.jvm_dir, where PMC downloads official versions of the Java Virtual Machine (JVM), it's specific to this launcher and does not exist in the official launcher.bin_dir, where temporary binaries (.dll, .so) are copied while game is running.
After constructing a context, all these attributes can be changed as you want.
from portablemc import Context
main_dir = "/opt/minecraft"
work_dir = "/home/john/minecraft"
ctx = Context(main_dir, work_dir)
print(ctx.work_dir) # "/home/foo/minecraft"
print(ctx.versions_dir) # "/opt/minecraft/versions"
print(ctx.assets_dir) # "/opt/minecraft/assets"
print(ctx.libraries_dir) # "/opt/minecraft/libraries"
print(ctx.jvm_dir) # "/opt/minecraft/jvm"
print(ctx.bin_dir) # "/home/foo/minecraft/bin"
# All these fields can be independently modified.A version manifest is an object that describes all online-available versions and how to download them. It also
gives the identifier of the latest snapshot and release. By default, this object will get its internal data
from https://launchermeta.mojang.com/mc/game/version_manifest.json, you can change this by extending the
class.
Since v2.2.0, it's also possible to use a cache file, the implementation then use If-Modified-Since HTTP
header to avoid download the manifest many times a day.
from portablemc import VersionManifest
cache_file = "/home/foo/minecraft/manifest.json"
cache_timeout = 5.0 # Maximum time when requesting online manifest
# cache_timeout = 0 # To disable online request, only local manifest is requested.
manifest = VersionManifest(cache_file, cache_timeout)
print(manifest.filter_latest("release")) # ("1.18.1", True) True = aliased, load the manifest
print(manifest.filter_latest("snapshot")) # ("21w44a", True)
print(manifest.filter_latest("1.17.1")) # ("1.17.1", False) False = not aliased, doesn't load the manifest
print(manifest.filter_latest("21w42a")) # ("21w42a", False)
# Following method always load the manifest.
print(manifest.get_version("release"))
print(manifest.get_version("1.18.1"))
# Both outputs: {'id': '1.18.1', 'type': 'release', 'url': '.../1.18.1.json', 'time': '2021-12-10T08:26:34+00:00', 'releaseTime': '2021-12-10T08:23:00+00:00'}
# If 'cache_timeout == 0', an error of type 'VersionManifestError' can be raised if not local copy exists.A version object in PMC is used to keep track of the installation of a version, this object is constructed by giving a Context object and the identifier (ID) of the version. The ID is the name of the directory where the version is installed, or if not installed, its identifier within its VersionManifest.
This class allows you to install a version step-by-step with the following methods:
prepare_meta(), prepare all versions metadata required to install this version (with inheritance) and computes the full flattened version metadata (without inheritance).prepare_jar(), prepare the version's JAR file if it's missing.prepare_assets(), prepare missing index and its assets (in context'sassets_dir).prepare_libraries(), prepare missing JAR libraries (in context'slibraries_dir).prepare_jvm(), prepare missing JVM files (in context'sjvm_dir).download(...), because all previous methods only "prepare" the launcher, actually downloading file is done via this method, it also improves global installation speed.
A simpler method install do all these steps for you (including download), it provides a keyword argument jvm
if you want to download the JVM.
After download is complete, you can use the start method to directly start the game, optionally you can pass
StartOptions, check the Start if you want to customize game starting.
Note that installing JVM is not required to launch the game.
from portablemc import Version, VersionError, JvmLoadingError
version_id = manifest.filter_latest("release") # Assume that '1.18.1' got returned.
version = Version(ctx, version_id)
try:
dl_report = version.install(jvm=True)
# Here, if not exception has been raised, the version is installed in directories
# specified by the context 'ctx'.
# You can check if some files has failed to download, using the returned "dl_report",
# which is of DownloadReport class (check sources).
except VersionError as e:
print(f"Version '{e.version}' not found.")
except JvmLoadingError as e:
print(f"No JVM found for you system.")A named tuple with options for starting the game, used when preparing process arguments. The following options are available:
auth_session, an optional AuthSession, if defined, it overridesuuidandusername.uuid, an optional offline UUID (without dashes).username, an optional offline username.demo, start Minecraft in demo mode (disabled by default).resolution, optional resolution for the game's window.disable_multiplayer, defaults toFalse(available since 1.16).disable_chat, defaults toFalse(available since 1.16).server_address, an optional server address to connect after Minecraft has started (available since 1.6).server_port, an optional server port, only used by the game if `server_address``is defined (available since 1.6).jvm_exec, an optional JVM executable, by default it use thejvm_execfrom the Version object.old_fix, enable JVM arguments that partially fix the game in alpha, beta, and release between 1.0 and 1.5 (included), enabled by default.features, a dictionary where you can define additional features, features are used to optionally change arguments version metadata files.
In the future, uuid and username could be integrated in a specific "offline auth session".
from portablemc import StartOptions
start_opts = StartOptions()
start_opts.disable_multiplayer = True
start_opts.username = "XxX_JohnDoe_XxX"
start_opts.resolution = (700, 500)A start object is can be used to prepare arguments before starting the game, it is constructed with a
Version object, after that you can use the prepare(opts) method with StartOptions.
After that you can use the start() method.
Start objects can be further customized to change the process runner or the binary directory path.
from portablemc import Start
start = Start(version)
start.prepare(start_opts)
start.main_class = "my.custom.main.Class" # If you need a custom main class to be executed
start.jvm_args.append("-Dmy.jvm.arg=89") # If you need to add more JVM arguments
start.game_args.append("--example") # If you need to add more game arguments
start.start()This is a base class for different types of authentication protocols. Currently, it's implemented for Yggdrasil (Mojang) and Microsoft protocols. This class is designed to be serialized and deserialized into a AuthDatabase, and provide some methods for retro-compatibility of the database. Actual objects can be used in StartOptions to allows client to have their skin and connect to online-mode servers.
An object linked to a database file, with explicit methods load(), save() and methods to manage its content:
get(email, sess_type), get a session by its email, and the session type (directly give the class).put(email, sess), put a session in the database.remove(email, sess_type), same asgetbut to remove a session from the database, returning it if existing.get_client_id(), can also be used to get a unique client ID (unique for the database) that you can use as aclient_idforauthenticatemethods.
from portablemc import AuthDatabase
auth_db = AuthDatabase("/home/foo/minecraft/")
auth_db.load()
# More examples for each type of session in the following sections.Implementation of the Yggdrasil (Mojang one) authentication protocol. Use authenticate(client_id, email, password)
to get a session object.
from portablemc import YggdrasilAuthSession, AuthError
try:
sess = YggdrasilAuthSession.authenticate(auth_db.get_client_id(), "foo.bar@example.com", "foobar")
start_opts.auth_session = sess
auth_db.put("foo.bar@example.com", sess)
auth_db.save()
except AuthError:
print("Failed to authenticate.")
# And later...
sess = auth_db.get("foo.bar@example.com", YggdrasilAuthSession)
# Or:
sess = auth_db.remove("foo.bar@example.com", YggdrasilAuthSession)
auth_db.save()Implementation of the Microsoft authentication protocol. This authentication protocol is a bit more complicated and requires to log-in from a web browser. First, you need an "app id" from https://portal.azure.com/, also check https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal. Remember to make a "public" application (without secret key), and to authorize the redirect URI that you want to use.
Once your application is ready, use the get_authentication_url(app_id, redirect_uri, email, nonce), note that
nonce can be used to check if the returned token ID is valid. Once you have a token, you can use
get_logout_url(app_id, redirect_uri) to clear the browser cache, the token will not be invalidated.
You should also use check_token_id(token_id, email, nonce) to check if the user has logged-in using the
email and nonce given in get_authentication_url.
Finally, you can use authenticate(client_id, app_id, code, redirect_uri) to get a session object.
A utility API that provides efficient download for sets of files.
An object that defines a single file to download, you should define when possible the expected size and/or SHA-1 hash of the file. You can also define a display name for CLI or interfaces.
A dynamic/growable list of DownloadEntry, when adding a download entry, public attributes count
and size and updated. You can also add callbacks functions that will be called if a download is successful. To
start the download, use download_files(...).
from portablemc import DownloadList, DownloadEntry, DownloadReport
dl_list = DownloadList()
dl_list.append(DownloadEntry("https://example.com/file1", "/tmp/file1"))
dl_list.append(DownloadEntry("http://example.com/file2", "/tmp/file2"))
report: DownloadReport = dl_list.download_files()