Reference / CT v4.3.0

Programmatic API.

Every module compiles against the host API in com.ct.main.api and cooperates with the other modules through the service registry. The desktop launcher and the command line are thin frontends over exactly the interfaces documented here — nothing in them is hidden or private by accident.

ModuleContext

The handle every module receives in CtModule.initialize(ModuleContext). A module is a jar with a ct.module.json manifest at its root and an entrypoint implementing com.ct.main.api.CtModule. The page below is the reference for what a module can call; build a runnable module is the end-to-end walkthrough, including the packaging that lets a downloaded module jar install itself into CT-Main.

public final class ExampleModule implements CtModule {
    @Override
    public void initialize(ModuleContext context) {
        context.console().writeLine("Example module loaded");
        context.registerView("Example", new ExamplePanel(context));
        context.publishService(ExampleService.class, new ExampleService());
    }
}
MemberDescription
mainVersion()The CT-Main version string.
applicationDirectory() / modulesDirectory()~/.ct-client and the active modules folder, absolute and normalized.
console()ConsoleWriter.writeLine(String) — the output view shared by every module and the host.
accounts()AccountProvider — the selected account and its listeners.
registerView(String title, JComponent)Adds a tab to the host window.
views()Every view registered so far, as ViewEntry(title, component).
publishService(Class<T>, T) / service(Class<T>)The service registry. Modules publish implementations and look up other modules' services, returning an Optional.
openWebsite() / openWebsite(String path)Opens https://ct.shit.pub, optionally at a path such as /modules.html.

SettingsAccess

Typed access to the non-secret settings at ~/.ct-client/settings.properties, published as a service by the settings module (com.ct.module.settings.SettingsStore). Credentials and access tokens are never part of it. Key names live in com.ct.main.api.SettingKeys.

SettingsAccess settings = context.service(SettingsAccess.class).orElseThrow();
Path gameDirectory = settings.gameDirectory();
int maxHeap = settings.number(SettingKeys.MEMORY_MAXIMUM, 4096);
settings.setFlag(SettingKeys.RECOMMENDED_JVM_FLAGS, true);
MemberDescription
text(String key, String fallback) / setText(String, String)String values. Setters throw IOException when the file cannot be written.
number(String key, int fallback) / setNumber(String, int)Integer values; invalid stored numbers fall back per key.
flag(String key, boolean fallback) / setFlag(String, boolean)Boolean values.
gameDirectory() / setGameDirectory(Path)Game directory, defaulting to <application directory>/game.
SettingKeysConstants for every key: GAME_DIRECTORY, MEMORY_MINIMUM, MEMORY_MAXIMUM, CUSTOM_RESOLUTION, RESOLUTION_WIDTH, RESOLUTION_HEIGHT, RECOMMENDED_JVM_FLAGS, OFFLINE_PLAYER_NAME, MICROSOFT_CLIENT_ID, MICROSOFT_LOGIN_MODE, JAVA_EXECUTABLE.

LaunchOptions

LaunchOptions options = new LaunchOptions(2048, 4096, false, 854, 480, false, true, "release");
FieldDescription
minimumMemoryMb, maximumMemoryMbHeap bounds, between MINIMUM_MEMORY_MB (1024) and MAXIMUM_MEMORY_MB (65536). The maximum must be at least the minimum.
customResolution, width, heightWhen enabled, the resolution must be at least 320 by 240, and the has_custom_resolution feature is reported to the game.
demoModeReports is_demo_user to the game.
recommendedJvmFlagsApplies Mojang's default-user-jvm entries before the version arguments.
versionTypeValue of version_type. "release" for a normal install.

Invalid arguments throw IllegalArgumentException from the record constructor. LaunchOptions.defaults() returns 2048/4096 MB, no custom resolution, 854 by 480, recommended flags on, "release". LaunchOptionsReader rebuilds an instance from the stored settings with per-key fallbacks.

Account

An immutable player identity. Offline profiles derive a stable UUID from the name, matching the convention used by other launchers. Account modules publish AccountProvider implementations that hand the selected Account to the launch pipeline.

Account offline = Account.offline("DevPlayer");
Account microsoft = context.service(AccountProvider.class)
        .flatMap(AccountProvider::selected)
        .orElse(null);
MemberDescription
Account.offline(String)Validates the name against [A-Za-z0-9_]{3,16} and throws IllegalArgumentException otherwise. The access token is 0 and the user type is legacy.
name, uuid, accessToken, xuid, clientId, userTypeIdentity fields passed to the game through launch placeholders.
isOffline()True for an offline profile.
label()Display name, with an offline suffix when applicable.

Never log, persist, or transmit accessToken. The launcher redacts it from any echoed command.

MinecraftInstaller

Lives in com.ct.module.installer. Resolves official metadata, downloads and verifies every game file, and installs the declared Java runtime.

MinecraftInstaller installer = new MinecraftInstaller(Path.of("/games/ct"));
MinecraftInstaller.VersionCatalog catalog = installer.versionCatalog();
MinecraftInstaller.Installation installation = installer.install(catalog.latestRelease(), System.out::println);
MinecraftInstaller.Installation existing = installer.existing("1.20.4");
boolean installed = installer.existing("1.20.4") != null;
MemberDescription
MinecraftInstaller(Path gameDirectory)Stores the directory, absolute and normalized.
gameDirectory()The resolved game directory.
versionCatalog()Loads Mojang's current official manifest and returns every safe release, snapshot, special, and legacy version ID plus the latest release ID.
install(String version, Consumer<String> status)Full install: version metadata, client jar, rule-matched libraries, asset index, all assets, log configuration, natives directories, and the official Java runtime. Returns the resulting Installation. Throws IOException when metadata, verification, or the runtime fails.
existing(String version)Rebuilds an Installation from files already on disk, or returns null when the metadata, client jar, or runtime is missing.
installedModLoaderProfiles() / installedFabricProfiles(String) / profileParent(String) / existingModLoaderProfile(String)Installed loader-profile discovery used by the launch pipeline's profile routing.
OfficialEndpoints.VERSION_MANIFEST, JAVA_RUNTIME_MANIFEST, ASSET_BASEThe official URLs that the installer reads.

The Installation record

Installation is a record with gameDirectory, versionDirectory, librariesDirectory, assetsDirectory, nativesDirectory, clientJar, runtimeExecutable, logConfig, classpath, assetIndexId, versionId, and the parsed metadata. withRuntimeExecutable(Path) returns a copy that uses a different Java executable, which is how a host JVM override is applied.

Installation guarantees

Every install through MinecraftInstaller upholds six guarantees:

  • Checksum and size verification. Downloads with published SHA-1 and size are checked against both. Legacy library downloads without those fields are restricted to HTTPS and rejected if empty; published legacy checksums are still checked.
  • No partial installs. Downloads are written to a .part file and moved into place only after verification.
  • HTTPS-only, path-safe sources. Only HTTPS sources are accepted, and paths from official metadata are rejected when they escape their target directory.
  • HTTP/1.1 with retries. Transfers use HTTP/1.1 with up to four attempts and backoff, because the asset CDN resets concurrent HTTP/2 streams.
  • Safe native extraction. Platform-matched native classifiers are downloaded separately from classpath artifacts and safely extracted to the natives directory. Metadata exclusion paths are honored, and archive paths cannot escape the destination.
  • Verified runtime completion. The runtime is written under runtimes/<component>/<version>, executable bits are applied, symbolic links are validated against the runtime root, and .ct-runtime-complete is written only after the whole runtime verifies.

LaunchService

Lives in com.ct.module.launcher together with CommandBuilder (argument construction), ProfileRouter (vanilla, CT-native, and installed loader profiles), and ProcessStarter. The launcher module publishes LaunchService as a service.

LaunchService service = context.service(LaunchService.class).orElseThrow();
List<String> command = service.buildCommand(null, account);
Process process = service.launch(null, account, line -> System.out.println(line));
int exitCode = process.waitFor();
MemberDescription
gameDirectory() / launchOptions()The game directory and launch options resolved from the settings.
resolveInstallation(String profile)Resolves the requested profile — installing or verifying first — and returns the Installation.
buildCommand(String profile, Account account)Returns the complete argument list. A null profile means the selected version. Supports modern argument arrays and legacy minecraftArguments, generates a missing classpath, and throws IOException for unsupported placeholders. An override overload takes explicit LaunchOptions.
launch(String profile, Account account, ConsoleWriter console)Resolves the installation, builds the command, appends game output to <game directory>/logs/ct-client-launch.log, forwards every line to the consumer, and returns the running Process. An override overload takes explicit LaunchOptions.

The command is runtimeExecutable, then the user JVM arguments, then the log configuration argument, then arguments.jvm, then the main class, then arguments.game. Rules are evaluated against the OS, architecture, and feature flags. An unknown placeholder such as ${unknown} is a hard failure instead of a silently dropped argument, so a metadata change surfaces immediately. The process starts through ProcessBuilder with the game directory as the working directory and no shell, so no argument is re-parsed or expanded.

RuleEvaluator

Lives in com.ct.module.rules with PlatformInfo. Evaluates Mojang rule objects, used for library selection during install and for argument selection during launch.

boolean allowed = RuleEvaluator.allows(libraryObject, RuleFeatures.none());
String os = PlatformInfo.operatingSystem();
MemberDescription
RuleEvaluator.allows(JsonObject element, RuleFeatures features)An element without rules is allowed. Otherwise the last matching rule wins, and an element with no matching rule is denied.
RuleFeatures(demoUser, customResolution, quickPlaysSupport, quickPlaySingleplayer, quickPlayMultiplayer, quickPlayRealms)Feature flags matched against a rule's features object. RuleFeatures.none() disables all of them.
PlatformInfo.operatingSystem()windows, osx, or linux, derived from os.name.
PlatformInfo.architecture()Raw os.arch value.
PlatformInfo.isArm(), is64Bit()Architecture predicates used for runtime and platform selection.

MicrosoftSignIn

Lives in com.ct.module.accountmicrosoft. OAuth 2.0 device code sign-in, followed by the Xbox Live, XSTS, and Minecraft services exchanges, and a profile check that confirms the account owns Java Edition. The built-in method needs no Azure application; the classic method uses a configured Azure public client ID.

MicrosoftSignIn signIn = new MicrosoftSignIn();
Account builtIn = signIn.signInDirect(status -> System.out.println(status));
Account custom = signIn.signIn(clientId, status -> System.out.println(status));

signInDirect(Consumer<String> status) signs in with the built-in client ID MicrosoftSignIn.BUILT_IN_CLIENT_ID (00000000402b5328) over the Live Connect device code endpoints at login.live.com, so the user signs in with the Microsoft account email and no Azure application is required. signIn(String clientId, Consumer<String> status) keeps the classic method over the Microsoft consumers tenant. Both report the user code and verification address through the consumer, open the browser, poll for the token at the interval the service returns, and return an Account with user type msa. Failures are reported as IOException with an actionable message, including the mapped XSTS cases such as a missing Xbox profile or a region restriction. The d= ticket prefix is applied only for the built-in launcher client ID; a third-party application sends its token unmodified.

A custom Azure application must be a public client with the device code flow enabled, allowed to use the XboxLive.signin and offline_access scopes. The built-in sign-in has no such requirements.

CT Loader mod API

CT's native mod interfaces live in com.ct.module.ctloader. CTMod is the entrypoint interface, CTModContext is handed to each entrypoint, CTModLoader scans mods/ and orders mods, and CTLoaderBootstrap runs in the game process before Minecraft's main class, passing the original game arguments through.

Each mod JAR carries a ct.mod.json at its root:

{
  "schemaVersion": 1,
  "id": "example",
  "name": "Example Mod",
  "version": "1.0.0",
  "entrypoints": ["example.ExampleMod"],
  "depends": []
}
InterfaceRole
CTModThe entrypoint interface. A public class with a public no-argument constructor implements it and receives the context in onInitialize.
CTModContextExposes the mod's parsed metadata, the game directory, the mods directory, and a mod-ID-prefixed logger through log.
CTModLoaderScans <game directory>/mods for JARs with a root-level ct.mod.json, checks required dependencies, rejects duplicate IDs and dependency cycles, and orders mods so dependencies initialize first.
CTLoaderBootstrapThe in-process bootstrap. It starts before Minecraft's official main class, initializes CT entrypoints in dependency order, then delegates to the game with the original arguments.

Dependency rules

  • Every ID in depends must be installed, or the launch stops with an error.
  • Duplicate mod IDs are rejected.
  • Dependency cycles are rejected — a cycle cannot be ordered.
  • Dependencies are initialized before the mods that depend on them.
  • A malformed mod or a failed entrypoint stops the launch with an error in the CT output.

The game classpath includes the ctloader module jar, so the bootstrap and mod API are available in the game process. CT's own API provides initialization and logging only — it does not transform Minecraft classes or provide Minecraft lifecycle hooks, and it is separate from Fabric, Forge, and NeoForge APIs.