Skip to content

Commit 9869a9a

Browse files
authored
Fully install a MultiMC instance (#95)
1 parent 03864b3 commit 9869a9a

File tree

13 files changed

+429
-98
lines changed

13 files changed

+429
-98
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Added full support for MultiMC installation. The MultiMC mode now creates an instance just like the Vanilla mode creates a profile.
10+
811
### Changed
912
- Reworked the way OptiFine is supported. Instead of searching for an installed OptiFine instance, the user provides us with an OptiFine installer jar.
1013
- `--minecraft-directory`'s aliases changed; `--launcher-dir` and `--launcher-directory` were added and `--mc-path` was removed.

src/main/java/io/github/ImpactDevelopment/installer/Args.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.github.ImpactDevelopment.installer.impact.ImpactVersionReleased;
2929
import io.github.ImpactDevelopment.installer.impact.ImpactVersions;
3030
import io.github.ImpactDevelopment.installer.setting.InstallationConfig;
31+
import io.github.ImpactDevelopment.installer.setting.Setting;
3132
import io.github.ImpactDevelopment.installer.setting.settings.*;
3233
import io.github.ImpactDevelopment.installer.target.InstallationModeOptions;
3334

@@ -66,6 +67,9 @@ public class Args {
6667
@Parameter(names = {"--mc-dir", "--minecraft-dir", "--minecraft-directory", "--launcher-dir", "--launcher-directory"}, description = "Path to the Minecraft Launcher directory")
6768
public String mcPath;
6869

70+
@Parameter(names = {"--mmc-dir", "--multimc-dir", "--multimc-directory", "--mmc-path"}, description = "Path to the MultiMC directory")
71+
public String multimcPath;
72+
6973
@Parameter(names = {"--optifine", "--of"}, description = "Path to an OptiFine installer jar")
7074
public String optifine;
7175

@@ -113,11 +117,10 @@ public Args() {
113117

114118
public void apply(InstallationConfig config) {
115119
if (mcPath != null) {
116-
Path path = Paths.get(mcPath);
117-
if (!Files.isDirectory(path)) {
118-
throw new IllegalStateException(path + " is not a directory");
119-
}
120-
config.setSettingValue(MinecraftDirectorySetting.INSTANCE, path);
120+
setDirectory(config, MinecraftDirectorySetting.INSTANCE, mcPath);
121+
}
122+
if (multimcPath != null) {
123+
setDirectory(config, MultiMCDirectorySetting.INSTANCE, multimcPath);
121124
}
122125
if (mode != null) {
123126
config.setSettingValue(InstallationModeSetting.INSTANCE, InstallationModeOptions.valueOf(mode.toUpperCase()));
@@ -151,6 +154,14 @@ public void apply(InstallationConfig config) {
151154
}
152155
}
153156

157+
private void setDirectory(InstallationConfig config, Setting<Path> setting, String value) {
158+
Path path = Paths.get(value);
159+
if (!Files.isDirectory(path)) {
160+
throw new IllegalStateException(path + " is not a directory");
161+
}
162+
config.setSettingValue(setting, path);
163+
}
164+
154165
private void setImpactVersion(InstallationConfig config, boolean checkMcVersionValidityAgainstReleases, ImpactVersion version) {
155166
config.setSettingValue(MinecraftVersionSetting.INSTANCE, version.mcVersion);
156167
if (checkMcVersionValidityAgainstReleases && !ImpactVersionSetting.INSTANCE.validSetting(config, version)) {

src/main/java/io/github/ImpactDevelopment/installer/gui/pages/MainPage.java

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import io.github.ImpactDevelopment.installer.setting.InstallationConfig;
2828
import io.github.ImpactDevelopment.installer.setting.Setting;
2929
import io.github.ImpactDevelopment.installer.setting.settings.*;
30+
import io.github.ImpactDevelopment.installer.target.InstallationModeOptions;
31+
import io.github.ImpactDevelopment.installer.utils.OperatingSystem;
3032

3133
import javax.swing.*;
3234
import java.awt.*;
@@ -36,42 +38,41 @@
3638
import java.nio.file.Path;
3739
import java.nio.file.Paths;
3840

41+
import static io.github.ImpactDevelopment.installer.target.InstallationModeOptions.MULTIMC;
42+
import static io.github.ImpactDevelopment.installer.target.InstallationModeOptions.SHOWJSON;
43+
import static io.github.ImpactDevelopment.installer.utils.OperatingSystem.OSX;
3944
import static javax.swing.JOptionPane.*;
4045

4146
public class MainPage extends JPanel {
4247
public MainPage(AppWindow app) {
48+
InstallationModeOptions mode = app.config.getSettingValue(InstallationModeSetting.INSTANCE);
49+
4350
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
4451
addSetting(InstallationModeSetting.INSTANCE, "Install for", app);
52+
if (mode == MULTIMC) addMultiMCSetting(app);
4553
addSetting(MinecraftVersionSetting.INSTANCE, "Minecraft version", app);
4654
addSetting(ImpactVersionSetting.INSTANCE, "Impact version", app);
47-
switch (app.config.getSettingValue(InstallationModeSetting.INSTANCE)) {
48-
case FORGE:
49-
case FORGE_PLUS_LITELOADER:
50-
break;
51-
default:
52-
addOptifineSetting(app);
55+
switch (mode) {
56+
case FORGE: case FORGE_PLUS_LITELOADER: break;
57+
default: addOptifineSetting(app);
5358
}
5459

5560
JButton install = new JButton("Install");
5661
install.addActionListener((ActionEvent) -> {
5762
try {
5863
String msg = app.config.execute();
59-
switch (app.config.getSettingValue(InstallationModeSetting.INSTANCE)) {
60-
case SHOWJSON:
61-
case MULTIMC:
62-
if (app.config.getSettingValue(OptiFineToggleSetting.INSTANCE)) {
63-
// Special case if installing optifine in showJson mode
64-
msg += "\nDo you want to install OptiFine's libs?";
65-
if (JOptionPane.showConfirmDialog(app, msg, "\uD83D\uDE0E", YES_NO_OPTION, INFORMATION_MESSAGE) == YES_OPTION) {
66-
msg = app.config.installOptifine();
67-
} else {
68-
// Only break the switch if no more msg to show, otherwise fallthrough
69-
break;
70-
}
71-
}
72-
default:
73-
JOptionPane.showMessageDialog(app, msg, "\uD83D\uDE0E", INFORMATION_MESSAGE);
64+
65+
// Special case if installing optifine in showJson mode
66+
if (mode == SHOWJSON && app.config.getSettingValue(OptiFineToggleSetting.INSTANCE)) {
67+
msg += "\nDo you want to install OptiFine's libs?";
68+
if (YES_OPTION == JOptionPane.showConfirmDialog(app, msg, "\uD83D\uDE0E", YES_NO_OPTION, INFORMATION_MESSAGE)) {
69+
msg = app.config.installOptifine();
70+
} else {
71+
return; // Early return if no more msg dialogs needed
72+
}
7473
}
74+
75+
JOptionPane.showMessageDialog(app, msg, "\uD83D\uDE0E", INFORMATION_MESSAGE);
7576
} catch (Throwable e) {
7677
app.exception(e);
7778
}
@@ -102,6 +103,11 @@ private <T> void addSetting(ChoiceSetting<T> setting, String text, AppWindow app
102103
add(container);
103104
}
104105

106+
private void addMultiMCSetting(AppWindow app) {
107+
String label = "MultiMC " + (OperatingSystem.getOS() == OSX ? "application" : "directory");
108+
add(buildPathSetting(MultiMCDirectorySetting.INSTANCE, label, JFileChooser.DIRECTORIES_ONLY, app));
109+
}
110+
105111
private void addOptifineSetting(AppWindow app) {
106112
OptiFineToggleSetting setting = OptiFineToggleSetting.INSTANCE;
107113
InstallationConfig config = app.config;

src/main/java/io/github/ImpactDevelopment/installer/impact/ImpactJsonVersion.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
*/
3838
public class ImpactJsonVersion {
3939
public String name; // will always be Impact :wink:
40+
public String id = "net.impactclient.Impact";
4041
public String version;
4142
public String mcVersion;
4243
public String mainClass = "net.minecraft.launchwrapper.Launch";
@@ -46,6 +47,7 @@ public class ImpactJsonVersion {
4647

4748
public void printInfo() {
4849
System.out.println(name);
50+
System.out.println(id);
4951
System.out.println(version);
5052
System.out.println(mcVersion);
5153
System.out.println(mainClass);

src/main/java/io/github/ImpactDevelopment/installer/optifine/OptiFine.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,14 @@ public String getLaunchwrapperID() {
164164
}
165165

166166
// Install optifine jar and launchwrapper (if required) to the target libraries directory
167-
public void install(Path libs, Path vanilla) throws IOException {
167+
public void install(Path libs, Path vanilla, boolean fullPath) throws IOException {
168168
try {
169-
installOptiFine(libs.resolve(MavenResolver.getPath(getOptiFineID())), vanilla);
169+
installOptiFine(libs.resolve(fullPath ? MavenResolver.getPath(getOptiFineID()) : MavenResolver.getFilename(getOptiFineID())), vanilla);
170170
} catch (InvocationTargetException | IllegalAccessException e) {
171171
throw new RuntimeException("Error calling OptiFine patcher method", e);
172172
}
173173
if (getLaunchwrapperID() != null) {
174-
installLaunchwrapper(libs.resolve(MavenResolver.getPath(getLaunchwrapperID())));
174+
installLaunchwrapper(libs.resolve(fullPath ? MavenResolver.getPath(getLaunchwrapperID()) : MavenResolver.getFilename(getLaunchwrapperID())));
175175
}
176176
}
177177

src/main/java/io/github/ImpactDevelopment/installer/setting/settings/MinecraftDirectorySetting.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import io.github.ImpactDevelopment.installer.utils.OperatingSystem;
2828

2929
import java.nio.file.Path;
30-
import java.nio.file.Paths;
3130

3231
public enum MinecraftDirectorySetting implements Setting<Path> {
3332
INSTANCE;
@@ -36,11 +35,11 @@ public enum MinecraftDirectorySetting implements Setting<Path> {
3635
public Path getDefaultValue(InstallationConfig config) {
3736
switch (OperatingSystem.getOS()) {
3837
case WINDOWS:
39-
return Paths.get(System.getenv("APPDATA")).resolve(".minecraft");
38+
return OperatingSystem.getDataDirectory().resolve(".minecraft");
4039
case OSX:
41-
return Paths.get(System.getProperty("user.home")).resolve("Library").resolve("Application Support").resolve("minecraft");
40+
return OperatingSystem.getDataDirectory().resolve("minecraft");
4241
default:
43-
return Paths.get(System.getProperty("user.home")).resolve(".minecraft");
42+
return OperatingSystem.getHome().resolve(".minecraft");
4443
}
4544
}
4645

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* This file is part of Impact Installer.
3+
*
4+
* Copyright (C) 2019 ImpactDevelopment and contributors
5+
*
6+
* See the CONTRIBUTORS.md file for a list of copyright holders
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1
11+
* of the License.
12+
*
13+
* This library is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
* Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public
19+
* License along with this library; if not, write to the Free Software
20+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
*/
22+
23+
package io.github.ImpactDevelopment.installer.setting.settings;
24+
25+
import io.github.ImpactDevelopment.installer.setting.InstallationConfig;
26+
import io.github.ImpactDevelopment.installer.setting.Setting;
27+
import io.github.ImpactDevelopment.installer.utils.OperatingSystem;
28+
29+
import java.io.IOException;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.nio.file.Paths;
33+
import java.util.Optional;
34+
35+
import static io.github.ImpactDevelopment.installer.utils.OperatingSystem.OSX;
36+
37+
public enum MultiMCDirectorySetting implements Setting<Path> {
38+
INSTANCE;
39+
40+
@Override
41+
public Path getDefaultValue(InstallationConfig config) {
42+
Path home = OperatingSystem.getHome();
43+
Path data = OperatingSystem.getDataDirectory();
44+
Path downloads = OperatingSystem.getDownloads();
45+
46+
// MultiMC is a portable app, so we can only guess its location.
47+
// On linux it can be installed via repos too, where it normally uses XDG_DATA_HOME, which is nice.
48+
switch (OperatingSystem.getOS()) {
49+
case LINUX:
50+
return scanForMultiMC(data, home, downloads).orElse(data.resolve("multimc"));
51+
case OSX:
52+
return scanForMultiMC(home.resolve("Applications"), Paths.get("/Applications")).orElse(downloads);
53+
default:
54+
return scanForMultiMC(home, data, home.resolve("Games"), downloads).orElse(home);
55+
}
56+
}
57+
58+
private static Optional<Path> scanForMultiMC(Path... locations) {
59+
for (Path folder : locations) {
60+
if (Files.isDirectory(folder)) {
61+
// Just in case, check if the search location itself is multimc
62+
if (isMultiMC(folder)) return Optional.of(folder);
63+
64+
// If not, check each of its children
65+
try {
66+
Optional<Path> match = Files.walk(folder, 1)
67+
.filter(MultiMCDirectorySetting::isMultiMC)
68+
.findFirst();
69+
if (match.isPresent()) {
70+
return match;
71+
}
72+
} catch (IOException ignored) { }
73+
}
74+
}
75+
return Optional.empty();
76+
}
77+
78+
private static boolean isMultiMC(Path path) {
79+
if (OperatingSystem.getOS() == OSX) {
80+
// On OSX, MultiMC nests its data within an app bundle
81+
path = path.resolve("Contents").resolve("MacOS");
82+
}
83+
// If multimc has been run in this location, even without completing setup, this file will exist
84+
Path config = path.resolve("multimc.cfg");
85+
return Files.isDirectory(path) && Files.isRegularFile(config);
86+
}
87+
88+
@Override
89+
public boolean validSetting(InstallationConfig config, Path value) {
90+
// we are ALL minecraft paths on this blessed day
91+
return true;
92+
}
93+
94+
@Override
95+
public String toString() {
96+
return getClass().getSimpleName();
97+
}
98+
}

src/main/java/io/github/ImpactDevelopment/installer/setting/settings/OptiFineSetting.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public List<String> getPossibleValues(InstallationConfig config) {
5555
}
5656
String minecraftVersion = config.getSettingValue(MinecraftVersionSetting.INSTANCE);
5757
Path minecraftDirectory = config.getSettingValue(MinecraftDirectorySetting.INSTANCE);
58+
// TODO also look through multimc for installed optifine?
5859

5960
List<String> result = new ArrayList<>();
6061
try {

src/main/java/io/github/ImpactDevelopment/installer/target/InstallationModeOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public String toString() {
6464
case SHOWJSON:
6565
return "Show Vanilla JSON";
6666
case MULTIMC:
67-
return "Show MultiMC JSON";
67+
return "MultiMC";
6868
case FORGE:
6969
return "Forge";
7070
case FORGE_PLUS_LITELOADER:

0 commit comments

Comments
 (0)