cli

package
v0.0.0-...-bfe662d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Account = &cli.Command{
	Name:  "account",
	Usage: "Manage Minecraft accounts",
	Commands: []*cli.Command{
		AccountLogin,
		AccountLogout,
		AccountLs,
	},
}
View Source
var AccountLogin = &cli.Command{
	Name:  "login",
	Usage: "Add a new account",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:  "local",
			Usage: "create an offline account",
		},
		&cli.StringFlag{
			Name:  "username",
			Usage: "username for offline account",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		localFlag := cmd.Bool("local")

		if localFlag {
			usernameFlag := cmd.String("username")

			if usernameFlag == "" {
				return fmt.Errorf("--local set but --username not set or empty")
			}

			if _, err := l.CreateOfflineAccount(usernameFlag); err != nil {
				return err
			}

			return nil
		}

		cb := &UILoginCallbacks{
			Writer: cmd.ErrWriter,
			IsTerm: false,
		}

		if f, ok := cmd.ErrWriter.(interface{ Fd() uintptr }); ok {
			cb.IsTerm = term.IsTerminal(int(f.Fd()))
		}

		ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
		defer cancel()

		mcAuth, err := authentication.AuthenticateMSA(ctx, l.HttpClient, cb)
		if err != nil {
			if errors.Is(err, context.Canceled) {
				return ErrExit(1)
			}

			return err
		}

		auth := launcher.Auth{
			Name:         mcAuth.Profile.Name,
			Uuid:         mcAuth.Profile.Id,
			Xuid:         mcAuth.Xuid,
			RefreshToken: mcAuth.RefreshToken,
		}
		authConfig := launcher.AccountConfig{}

		if len(auth.Uuid) == 32 {
			parts := []string{
				auth.Uuid[0:8],
				auth.Uuid[8:12],
				auth.Uuid[12:16],
				auth.Uuid[16:20],
				auth.Uuid[20:32],
			}
			auth.Uuid = strings.Join(parts, "-")
		}

		account, err := l.CreateOnlineAccount(auth, &authConfig)
		if err != nil {
			return err
		}

		fmt.Fprintf(cmd.Writer, "Username: %s\n", account.Username)
		fmt.Fprintf(cmd.Writer, "UUID    : %s\n", account.Uuid)

		return nil

	},
}
View Source
var AccountLogout = &cli.Command{
	Name:  "logout",
	Usage: "Remove an account",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "force",
			Aliases: []string{"f"},
		},
		&cli.BoolFlag{
			Name:    "verbose",
			Aliases: []string{"v"},
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		if cmd.Args().Len() == 0 {
			return cli.ShowSubcommandHelp(cmd)
		}

		forceFlag := cmd.Bool("force")
		verboseFlag := cmd.Bool("verbose")

		for _, accountName := range cmd.Args().Slice() {
			if err := l.DeleteAccount(accountName); err != nil {
				if forceFlag {
					continue
				}
				return err
			}

			if verboseFlag {
				fmt.Fprintf(cmd.Writer, "removed account %q\n", accountName)
			}
		}
		return nil
	},
}
View Source
var AccountLs = &cli.Command{
	Name:  "ls",
	Usage: "List accounts",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "quiet",
			Aliases: []string{"q"},
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		quietFlag := cmd.Bool("quiet")

		results, err := l.GetAccounts()
		if err != nil {
			return err
		}

		w := tabwriter.NewWriter(cmd.Writer, 1, 4, 3, ' ', 0)

		if !quietFlag {
			fmt.Fprintf(w, "USERNAME\tUUID\tTYPE\tENCRYPTION\tEXPIRES\tDEFAULT\n")
		}

		for _, res := range results {
			if res.Error != nil {
				path, err := filepath.Rel(l.BaseDir, res.Path)
				if err != nil {
					path = res.Path
				}

				fmt.Fprintf(cmd.ErrWriter, "warning: failed to load %q: %v\n", path, res.Error)
			} else {
				account := &res.Account

				if quietFlag {
					fmt.Fprintln(w, account.Username)
				} else {
					isDefault := ""
					fmt.Fprintf(w, "%s\t%s\toffline\tnone\tnever\t%s\n", account.Username, account.Uuid, isDefault)
				}
			}
		}

		w.Flush()

		return nil
	},
}
View Source
var Instance = &cli.Command{
	Name:  "instance",
	Usage: "Manage game intances",
	Commands: []*cli.Command{
		InstanceCreate,
		InstanceList,

		InstanceMod,
		InstanceRemove,
		InstanceRename,
		InstanceSetAccount,
		InstanceShow,
		InstanceStart,
	},
}
View Source
var InstanceCreate = &cli.Command{
	Name:      "create",
	Usage:     "Create a new instance",
	ArgsUsage: "INSTANCE",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:  "version",
			Value: "latest",
			Usage: "choose Minecraft version",
		},
		&cli.StringFlag{
			Name:  "loader",
			Usage: "choose mod loader",
		},
		&cli.StringFlag{
			Name:  "java",
			Usage: "choose alternative Java path",
		},
		&cli.StringFlag{
			Name:  "account",
			Usage: "choose alternative account",
		},
		&cli.BoolFlag{
			Name:  "fetch",
			Usage: "fetch assets and libraries",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		if cmd.Args().Len() != 1 {
			return cli.ShowSubcommandHelp(cmd)
		}

		instanceArg := cmd.Args().Get(0)

		loaderFlag := cmd.String("loader")
		versionFlag := cmd.String("version")
		javaFlag := cmd.String("java")

		loaderTypeString, loaderVersion, _ := strings.Cut(loaderFlag, "@")

		var loaderType launcher.ModLoaderType

		if err := loaderType.UnmarshalText([]byte(loaderTypeString)); err != nil {
			return err
		}

		loader := &launcher.ModLoader{
			Type:    loaderType,
			Version: loaderVersion,
		}

		l := GetLauncher(ctx)

		ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
		defer cancel()

		manifest, err := l.GetManifest()
		if err != nil {
			return err
		}

		var versionId string

		if versionFlag == "latest" {
			ver, ok := manifest.GetLatestRelease()
			if !ok {
				return fmt.Errorf("no latest release available")
			}

			versionId = ver.Id
		} else {
			versionId = versionFlag
		}

		version, err := l.GetVersion(ctx, versionId)
		if err != nil {
			return err
		}

		instance, err := l.CreateInstanceEx(instanceArg, loaderFlag, version, func(config *launcher.InstanceConfig) error {
			config.JavaPath = javaFlag
			config.AdditionalClasspath = make([]string, 0)
			config.ModLoader = loader
			config.Mods = make(map[string]launcher.InstanceConfigMod)
			return nil
		})
		if err != nil {
			return err
		}

		if cmd.Bool("fetch") {
			var progress ProgressBarReporter

			if err := l.DownloadLibraries(ctx, instance, &progress); err != nil {
				return err
			}

			if err := l.DownloadAssets(ctx, instance, &progress); err != nil {
				return err
			}
		}

		return nil

	},
}
View Source
var InstanceList = &cli.Command{
	Name:    "ls",
	Aliases: []string{"list"},
	Usage:   "List available instances",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "quiet",
			Aliases: []string{"q"},
			Usage:   "only print instance names",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		results, err := l.GetInstances()
		if err != nil {
			return err
		}

		quietFlag := cmd.Bool("quiet")

		w := tabwriter.NewWriter(cmd.Writer, 1, 4, 3, ' ', 0)

		if !quietFlag {
			fmt.Fprintf(w, "INSTANCE\tVERSION\tLOADER\tACCOUNT\n")
		}

		for _, res := range results {
			instanceName := filepath.Base(res.Path)

			if res.Error != nil {
				path, err := filepath.Rel(l.BaseDir, res.Path)
				if err != nil {
					path = res.Path
				}

				fmt.Fprintf(cmd.ErrWriter, "warning: failed to load %q: %v\n", path, res.Error)
			} else {
				config := &res.Instance

				if quietFlag {
					fmt.Fprintln(w, instanceName)
				} else {
					fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", instanceName, config.Version, "vanilla", config.Account)
				}
			}
		}

		w.Flush()
		return nil

	},
}
View Source
var InstanceMod = &cli.Command{
	Name:  "mod",
	Usage: "Manage instance mods",
	Commands: []*cli.Command{
		InstanceModAdd,
		InstanceModLs,
		InstanceModPin,
		InstanceModRm,
	},
}
View Source
var InstanceModAdd = &cli.Command{
	Name:      "add",
	Usage:     "Install mods",
	ArgsUsage: "INSTANCE MOD[@<VERSION> [MOD[@<VERSION>]...]",
	Action: func(ctx context.Context, cmd *cli.Command) error {
		panic("")
	},
}
View Source
var InstanceModLs = &cli.Command{
	Name:      "ls",
	Aliases:   []string{"list"},
	Usage:     "List installed mods",
	ArgsUsage: "INSTANCE",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "quiet",
			Aliases: []string{"q"},
			Usage:   "only print mod \"id@version\"",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		if cmd.Args().Len() != 1 {
			return cli.ShowSubcommandHelp(cmd)
		}

		instanceArg := cmd.Args().Get(0)

		l := GetLauncher(ctx)

		instance, err := l.GetInstance(instanceArg)
		if err != nil {
			return err
		}

		mods, err := l.GetMods(instance, instance.Config.ModLoader.Type)
		if err != nil {
			return err
		}

		w := tabwriter.NewWriter(cmd.Writer, 1, 4, 3, ' ', 0)

		fmt.Fprintln(w, "ID\tVERSION\tLOADER")

		for _, mod := range mods {
			fmt.Fprintf(w, "%s\t%s\t%s\n", mod.ModID(), mod.ModVersion(), mod.ModLoader())
		}

		w.Flush()
		return nil
	},
}
View Source
var InstanceModPin = &cli.Command{
	Name:      "pin",
	Usage:     "Assign specific mod versions",
	ArgsUsage: "INSTANCE MOD[@<VERSION> [MOD[@<VERSION>]...]",
	Action: func(ctx context.Context, cmd *cli.Command) error {
		return nil
	},
}
View Source
var InstanceModRm = &cli.Command{
	Name:      "rm",
	Aliases:   []string{"remove"},
	Usage:     "Uninstall mods",
	ArgsUsage: "INSTANCE MOD [MOD...]",
	Action: func(ctx context.Context, cmd *cli.Command) error {
		return nil
	},
}
View Source
var InstanceRemove = &cli.Command{
	Name:      "rm",
	Aliases:   []string{"remove"},
	Usage:     "Delete an existing instance",
	ArgsUsage: "INSTANCE [INSTANCES...]",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "force",
			Aliases: []string{"f"},
			Usage:   "ignore non-existing instances",
		},
		&cli.BoolFlag{
			Name:    "verbose",
			Aliases: []string{"v"},
			Usage:   "print extra messages",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		if cmd.Args().Len() < 1 {
			return cli.ShowSubcommandHelp(cmd)
		}

		l := GetLauncher(ctx)
		hasErr := false

		forceFlag := cmd.Bool("force")
		verboseFlag := cmd.Bool("verbose")

		for _, instance := range cmd.Args().Slice() {
			err := l.DeleteInstance(instance)

			if err != nil {
				if errors.Is(err, os.ErrNotExist) || !forceFlag {
					hasErr = true
					fmt.Fprintf(cmd.ErrWriter, "cannot delete %q: %v\n", instance, err)
				}
			} else {
				if verboseFlag {
					fmt.Printf("removed instance %q\n", instance)
				}
			}
		}

		if hasErr {
			return ErrExit(1)
		}

		return nil
	},
}
View Source
var InstanceRename = &cli.Command{
	Name:      "rename",
	Usage:     "Rename an existing instance",
	ArgsUsage: "OLD NEW",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "verbose",
			Aliases: []string{"v"},
			Usage:   "",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		if cmd.Args().Len() != 2 {
			return cli.ShowSubcommandHelp(cmd)
		}

		verboseFlag := cmd.Bool("verbose")

		oldArg := cmd.Args().Get(0)
		newArg := cmd.Args().Get(1)

		if err := l.RenameInstance(oldArg, newArg); err != nil {
			if errors.Is(err, os.ErrNotExist) {
				return fmt.Errorf("%s: no such instance", oldArg)
			}

			return err
		}

		if verboseFlag {
			fmt.Printf("%q -> %q\n", oldArg, newArg)
		}

		return nil
	},
}
View Source
var InstanceSetAccount = &cli.Command{
	Name:      "set-account",
	Usage:     "Set account for an instance",
	ArgsUsage: "INSTANCE ACCOUNT",
	Action: func(ctx context.Context, cmd *cli.Command) error {
		if cmd.Args().Len() != 2 {
			return cli.ShowSubcommandHelp(cmd)
		}

		instanceArg := cmd.Args().Get(0)
		accountArg := cmd.Args().Get(1)

		l := GetLauncher(ctx)

		ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
		defer cancel()

		instance, err := l.LoadInstance(ctx, instanceArg)
		if err != nil {
			if errors.Is(err, context.Canceled) {
				return ErrExit(1)
			}
			return err
		}

		if _, err := l.GetAccount(accountArg); err != nil {
			return err
		}

		err = l.EditInstance(instance, func(conf *launcher.InstanceConfig) error {
			conf.Account = accountArg
			return nil
		})
		if err != nil {
			return err
		}

		return nil
	},
}
View Source
var InstanceShow = &cli.Command{
	Name:      "show",
	Usage:     "Print information about an instance",
	ArgsUsage: "INSTANCE",
	Action: func(ctx context.Context, cmd *cli.Command) error {
		if cmd.Args().Len() != 1 {
			return cli.ShowSubcommandHelp(cmd)
		}

		l := GetLauncher(ctx)

		ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
		defer cancel()

		instanceArg := cmd.Args().Get(0)

		instance, err := l.LoadInstance(ctx, instanceArg)
		if err != nil {
			return err
		}

		fmt.Printf("Path: %s\n", instance.Dir)
		fmt.Printf("Version: %s\n", instance.Config.Version)

		return nil
	},
}
View Source
var InstanceStart = &cli.Command{
	Name:      "start",
	Usage:     "Start an instance",
	ArgsUsage: "INSTANCE",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:  "no-libs",
			Usage: "don't attempt to fetch assets",
		},
		&cli.BoolFlag{
			Name:  "no-assets",
			Usage: "don't attempt to fetch libraries",
		},
		&cli.StringFlag{
			Name:  "account",
			Usage: "use alternative account",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		if cmd.Args().Len() != 1 {
			return cli.ShowSubcommandHelp(cmd)
		}

		instanceArg := cmd.Args().Get(0)

		accountFlag := cmd.String("account")
		noAssetsFlag := cmd.Bool("no-assets")
		noLibsFlag := cmd.Bool("no-libs")

		l := GetLauncher(ctx)

		ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
		defer cancel()

		instance, err := l.LoadInstance(ctx, instanceArg)
		if err != nil {
			return err
		}

		var account *launcher.Account

		if accountFlag != "" {
			account, err = l.GetAccount(accountFlag)
		} else if instance.Config.Account != "" {
			account, err = l.GetAccount(instance.Config.Account)
		} else {
			account, err = l.GetDefaultAccount()
		}

		if err != nil {
			return err
		}

		var auth *launcher.Auth

		if l.Offline || account.IsOffline() {
			auth = &launcher.Auth{
				Name: account.Username,
				Uuid: account.Uuid,
				Xuid: account.Xuid,
			}
		} else {
			auth, err = l.AuthenticateAccount(account)
			if err != nil {
				return err
			}
		}

		var reporter ProgressBarReporter

		if !l.Offline && !noLibsFlag {
			if err := l.DownloadLibraries(ctx, instance, &reporter); err != nil {
				return fmt.Errorf("failed to download libraries for instance %q: %w", instanceArg, err)
			}
		}

		if !l.Offline && !noAssetsFlag {
			if err := l.DownloadAssets(ctx, instance, &reporter); err != nil {
				return fmt.Errorf("failed to download assets for instance %q: %w", instanceArg, err)
			}
		}

		command, err := l.CreateCommand(ctx, instance, auth)
		if err != nil {
			return fmt.Errorf("failed to create command for instance %q: %w\n", instanceArg, err)
		}

		if err := command.Start(); err != nil {
			return fmt.Errorf("failed to start process for instance %q: %w\n", instanceArg, err)
		}

		if err := command.Wait(); err != nil {
			if exitErr, ok := err.(*exec.ExitError); ok {
				return exitErr
			} else {
				return fmt.Errorf("failed to wait process for instance %q: %w", instanceArg, err)
			}
		}

		return nil

	},
}
View Source
var Java = &cli.Command{
	Name:  "java",
	Usage: "Manage Java installations",
	Commands: []*cli.Command{
		JavaLs,
	},
}
View Source
var JavaLs = &cli.Command{
	Name:  "ls",
	Usage: "List available Java installation paths",
	Action: func(_ context.Context, cmd *cli.Command) error {
		javaPaths := java.GetJavaPaths()

		w := tabwriter.NewWriter(cmd.Writer, 1, 4, 3, ' ', 0)

		fmt.Fprintf(w, "SOURCE\tVERSION\tVENDOR\n")

		for _, path := range javaPaths {
			value := path.Value
			version := path.Version
			vendor := path.Vendor

			if version == "" {
				version = "unknown"
			}

			if vendor == "" {
				vendor = "unknown"
			}

			fmt.Fprintf(w, "%s\t%s\t%s\n", value, version, vendor)
		}

		w.Flush()
		return nil
	},
}
View Source
var Minecraft = &cli.Command{
	Name: "minecraft",
	Commands: []*cli.Command{
		MinecraftLs,
		MinecraftUpdate,
	},
}
View Source
var MinecraftLs = &cli.Command{
	Name: "ls",
	Flags: []cli.Flag{
		&cli.StringSliceFlag{
			Name:  "type",
			Value: []string{"release"},
		},
		&cli.BoolFlag{
			Name:    "quiet",
			Aliases: []string{"q"},
			Usage:   "only print version ids",
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		quietFlag := cmd.Bool("quiet")
		typeFlag := cmd.StringSlice("type")

		versionTypes := make(map[string]struct{}, 4)
		all := slices.Contains(typeFlag, "all")

		if !all {
			for _, versionType := range typeFlag {
				switch versionType {
				case "release":
					versionTypes["release"] = struct{}{}
				case "snapshot":
					versionTypes["snapshot"] = struct{}{}
				case "beta":
					versionTypes["old_beta"] = struct{}{}
				case "alpha":
					versionTypes["old_alpha"] = struct{}{}
				}
			}
		}

		manifest, err := l.GetManifest()
		if err != nil {
			if errors.Is(err, os.ErrNotExist) {
				return fmt.Errorf("index does not exist, try running `muxi minecraft update`")
			}
			return err
		}

		output := cmd.Writer

		if f, ok := output.(interface{ Fd() uintptr }); ok {
			if term.IsTerminal(int(f.Fd())) {
				pager, err := ui.SpawnPager(context.Background())
				if err != nil {
					fmt.Fprintf(cmd.ErrWriter, "warning: failed to spawn pager: %v\n", err)
				} else {
					defer pager.Close()
					output = pager
				}
			}
		}

		w := tabwriter.NewWriter(output, 1, 4, 3, ' ', 0)
		defer w.Flush()

		if !quietFlag {
			fmt.Fprintln(w, "ID\tTYPE\tRELEASED\tHASH")

		}

		for _, version := range manifest.Versions {
			if !all {
				if _, ok := versionTypes[version.Type]; !ok {
					continue
				}
			}

			if quietFlag {
				fmt.Println(version.Id)
			} else {
				timeOffset := timeOffsetConfig.Format(version.ReleaseTime)
				var versionType string

				switch version.Type {
				case "release", "snapshot":
					versionType = version.Type
				case "old_beta":
					versionType = "beta"
				case "old_alpha":
					versionType = "alpha"
				}

				fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", version.Id, versionType, timeOffset, version.Sha1)
			}
		}

		return nil
	},
}
View Source
var MinecraftUpdate = &cli.Command{
	Name:  "update",
	Usage: "update version index",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:    "force",
			Aliases: []string{"f"},
		},
	},
	Action: func(ctx context.Context, cmd *cli.Command) error {
		l := GetLauncher(ctx)

		if l.Offline {
			return fmt.Errorf("offline mode enabled")
		}

		forceFlag := cmd.Bool("force")

		ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
		defer cancel()

		currentManifest, err := l.GetManifest()
		if err != nil {
			currentManifest = new(vanilla.Manifest)
		}

		newManifest, err := l.UpdateManifest(ctx)
		if err != nil {
			return err
		}

		type summary struct {
			added    uint
			modified uint
		}

		type versionInfo struct {
			hash    string
			summary *summary
		}

		versionMap := make(map[string]string, len(newManifest.Versions))

		var releases summary
		var snapshots summary
		var other summary

		if !forceFlag {
			for _, version := range currentManifest.Versions {
				versionMap[version.Id] = version.Sha1
			}
		}

		for _, version := range newManifest.Versions {
			var sum *summary

			switch version.Type {
			case "release":
				sum = &releases
			case "snapshot":
				sum = &snapshots
			default:
				sum = &other
			}

			hash, ok := versionMap[version.Id]
			switch {
			case !ok:
				sum.added += 1
			case hash != version.Sha1:
				sum.modified += 1
			}

			delete(versionMap, version.Id)
		}

		changed := false

		if releases.added != 0 || releases.modified != 0 {
			fmt.Printf("releases +%d ~%d\n", releases.added, releases.modified)
			changed = true
		}

		if snapshots.added != 0 || snapshots.modified != 0 {
			fmt.Printf("snapshots +%d ~%d\n", snapshots.added, snapshots.modified)
			changed = true
		}

		if other.added != 0 || other.modified != 0 {
			fmt.Printf("other +%d ~%d\n", other.added, other.modified)
			changed = true
		}

		if !changed {
			fmt.Println("index up to date")
		}

		return nil
	},
}
View Source
var Root = &cli.Command{
	Name:  "muxi",
	Usage: "Unofficial launcher for Minecraft: Java Edition",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name: "home",
		},
		&cli.DurationFlag{
			Name:  "http-timeout",
			Usage: "timeout for HTTP requests in seconds",
			Value: 30 * time.Second,
		},
		&cli.UintFlag{
			Name:  "asset-jobs",
			Usage: "concurrent jobs for asset downloads",
			Value: 64,
		},
		&cli.UintFlag{
			Name:  "library-jobs",
			Usage: "concurrent jobs for library downloads",
			Value: 32,
		},
		&cli.BoolFlag{
			Name:  "offline",
			Usage: "never try accessing network",
		},
		&cli.BoolFlag{
			Name:   "staging",
			Usage:  "use staging APIs if available (FOR TESTING ONLY)",
			Hidden: true,
		},
		&cli.BoolFlag{
			Name:  "version",
			Usage: "displays version and exit",
		},
	},
	EnableShellCompletion: true,
	Commands: []*cli.Command{
		Instance,
		Account,
		Java,
		Minecraft,
		Version,
	},
	Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
		if cmd.Bool("version") {
			if err := Version.Action(ctx, Version); err != nil {
				return ctx, err
			}

			return ctx, ErrExit(0)
		}

		hc := &http.Client{
			Timeout: cmd.Duration("http-timeout"),
		}

		launcherHome := cmd.String("home")

		if launcherHome == "" {
			home, err := getLauncherHome()
			if err != nil {
				return ctx, err
			}

			launcherHome = home
		}

		l := &launcher.Launcher{
			BaseDir:     launcherHome,
			HttpClient:  hc,
			LibraryJobs: cmd.Uint("library-jobs"),
			AssetJobs:   cmd.Uint("asset-jobs"),
			Modrinth: modrinth.Client{
				HttpClient: hc,
				Staging:    cmd.Bool("staging"),
			},
			Offline: cmd.Bool("offline"),
		}

		return context.WithValue(ctx, launcherKey{}, l), nil
	},
}
View Source
var Version = &cli.Command{
	Name: "version",
	Action: func(context.Context, *cli.Command) error {
		fmt.Printf("MuxiMC version %s, revision %s\n", vcsTag, vcsRev)
		return nil
	},
}

Functions

func ErrExit

func ErrExit(code int) error

func GetLauncher

func GetLauncher(ctx context.Context) *launcher.Launcher

Types

type ExitNoMessage

type ExitNoMessage struct {
	Code int
}

func (*ExitNoMessage) Error

func (err *ExitNoMessage) Error() string

type ProgressBarReporter

type ProgressBarReporter struct {
	// contains filtered or unexported fields
}

func (*ProgressBarReporter) End

func (r *ProgressBarReporter) End()

func (*ProgressBarReporter) Increment

func (r *ProgressBarReporter) Increment()

func (*ProgressBarReporter) Start

func (r *ProgressBarReporter) Start(stage string, max int)

type UILoginCallbacks

type UILoginCallbacks struct {
	Writer io.Writer
	IsTerm bool
}

func (*UILoginCallbacks) DeviceCode

func (cb *UILoginCallbacks) DeviceCode(url string, code string, expiryMinutes int)

func (*UILoginCallbacks) Init

func (cb *UILoginCallbacks) Init(steps int)

func (*UILoginCallbacks) Message

func (cb *UILoginCallbacks) Message(text string)

func (*UILoginCallbacks) Step

func (cb *UILoginCallbacks) Step(name string)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL