├── README.md ├── settings.gradle ├── DS_Store ├── ftbl.icns ├── ftbl.ico ├── ftbl.png ├── .gitattributes ├── installig.png ├── .gitmodules ├── lib └── i4jruntime.jar ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── java │ │ └── net │ │ │ └── creeperhost │ │ │ └── creeperlauncher │ │ │ ├── api │ │ │ ├── data │ │ │ │ ├── instances │ │ │ │ │ ├── SyncInstanceData.java │ │ │ │ │ ├── MessageClientData.java │ │ │ │ │ ├── InstanceSetModEnabledData.java │ │ │ │ │ ├── BrowseInstanceData.java │ │ │ │ │ ├── LaunchInstanceData.java │ │ │ │ │ ├── UninstallInstanceData.java │ │ │ │ │ ├── InstanceModsData.java │ │ │ │ │ ├── InstanceConfigureData.java │ │ │ │ │ ├── ShareInstanceData.java │ │ │ │ │ ├── CancelInstallInstanceData.java │ │ │ │ │ ├── InstalledInstancesData.java │ │ │ │ │ ├── UpdateInstanceData.java │ │ │ │ │ ├── InstanceInfoData.java │ │ │ │ │ ├── InstallInstanceData.java │ │ │ │ │ └── InstanceInstallModData.java │ │ │ │ ├── BaseData.java │ │ │ │ ├── irc │ │ │ │ │ ├── IRCQuitRequestData.java │ │ │ │ │ ├── IRCSendMessageData.java │ │ │ │ │ ├── IRCConnectData.java │ │ │ │ │ ├── IRCPartyInviteData.java │ │ │ │ │ └── IRCEventMessageData.java │ │ │ │ ├── friends │ │ │ │ │ ├── BlockFriendData.java │ │ │ │ │ ├── AddFriendData.java │ │ │ │ │ ├── OnlineFriendData.java │ │ │ │ │ ├── AcceptedFriendData.java │ │ │ │ │ ├── FriendData.java │ │ │ │ │ └── GetFriendsData.java │ │ │ │ └── other │ │ │ │ │ ├── PingLauncherData.java │ │ │ │ │ ├── PongLauncherData.java │ │ │ │ │ ├── CloseModalData.java │ │ │ │ │ ├── YeetLauncherData.java │ │ │ │ │ ├── StoreAuthDetailsData.java │ │ │ │ │ ├── GetJavasData.java │ │ │ │ │ ├── InstalledFileEventData.java │ │ │ │ │ ├── FileHashData.java │ │ │ │ │ ├── SettingsConfigureData.java │ │ │ │ │ ├── UploadLogsData.java │ │ │ │ │ ├── ClientLaunchData.java │ │ │ │ │ ├── SettingsInfoData.java │ │ │ │ │ └── OpenModalData.java │ │ │ ├── handlers │ │ │ │ ├── IMessageHandler.java │ │ │ │ ├── instances │ │ │ │ │ ├── UpdateInstanceHandler.java │ │ │ │ │ ├── CancelInstallInstanceHandler.java │ │ │ │ │ ├── InstanceSetModEnabledHandler.java │ │ │ │ │ ├── LaunchInstanceHandler.java │ │ │ │ │ ├── BrowseInstanceHandler.java │ │ │ │ │ ├── MessageClientHandler.java │ │ │ │ │ ├── UninstallInstanceHandler.java │ │ │ │ │ ├── InstalledInstancesHandler.java │ │ │ │ │ ├── InstanceInfoHandler.java │ │ │ │ │ ├── ShareInstanceHandler.java │ │ │ │ │ ├── InstanceModsHandler.java │ │ │ │ │ ├── InstanceConfigureHandler.java │ │ │ │ │ └── SyncInstanceHandler.java │ │ │ │ ├── friends │ │ │ │ │ ├── AddFriendHandler.java │ │ │ │ │ ├── GetFriendsHandler.java │ │ │ │ │ └── BlockFriendHandler.java │ │ │ │ ├── other │ │ │ │ │ ├── YeetLauncherHandler.java │ │ │ │ │ ├── PongLauncherHandler.java │ │ │ │ │ ├── GetJavasHandler.java │ │ │ │ │ ├── SettingsInfoHandler.java │ │ │ │ │ ├── ModalCallbackHandler.java │ │ │ │ │ ├── UploadLogsHandler.java │ │ │ │ │ ├── FileHashHandler.java │ │ │ │ │ ├── SettingsConfigureHandler.java │ │ │ │ │ └── StoreAuthDetailsHandler.java │ │ │ │ ├── irc │ │ │ │ │ ├── IRCQuitRequestHandler.java │ │ │ │ │ ├── IRCSendMessageHandler.java │ │ │ │ │ └── IRCConnectHandler.java │ │ │ │ └── ModFile.java │ │ │ ├── SimpleDownloadableFile.java │ │ │ └── WebSocketAPI.java │ │ │ ├── install │ │ │ └── tasks │ │ │ │ ├── http │ │ │ │ ├── IProgressUpdater.java │ │ │ │ ├── IHttpClient.java │ │ │ │ ├── DownloadedFile.java │ │ │ │ ├── OkHttpClientImpl.java │ │ │ │ └── JavaHttpClientImpl.java │ │ │ │ └── IInstallTask.java │ │ │ ├── os │ │ │ ├── OSUtils.java │ │ │ ├── platform │ │ │ │ ├── window │ │ │ │ │ ├── IMonitor.java │ │ │ │ │ ├── win │ │ │ │ │ │ ├── WindowsMonitor.java │ │ │ │ │ │ ├── WindowsWindowHelper.java │ │ │ │ │ │ └── WindowsWindow.java │ │ │ │ │ ├── IWindow.java │ │ │ │ │ └── IWindowHelper.java │ │ │ │ ├── UnixPlatform.java │ │ │ │ ├── UnknownPlatform.java │ │ │ │ ├── WindowsPlatform.java │ │ │ │ ├── BasePlatform.java │ │ │ │ ├── LinuxPlatform.java │ │ │ │ └── MacosPlatform.java │ │ │ ├── PlatformNotSupportedException.java │ │ │ └── OS.java │ │ │ ├── util │ │ │ ├── Pair.java │ │ │ ├── ElapsedTimer.java │ │ │ ├── instanceEvent.java │ │ │ ├── StreamGobblerLog.java │ │ │ ├── ImageUtils.java │ │ │ ├── SettingsChangeUtil.java │ │ │ ├── LoaderTarget.java │ │ │ ├── Artifact.java │ │ │ ├── GsonUtils.java │ │ │ ├── DownloadUtils.java │ │ │ └── LogsUploader.java │ │ │ ├── pack │ │ │ ├── IPack.java │ │ │ └── ModPack.java │ │ │ ├── Analytics.java │ │ │ ├── minecraft │ │ │ ├── modloader │ │ │ │ ├── forge │ │ │ │ │ ├── ForgeModLoader.java │ │ │ │ │ ├── ForgeInstallerModLoader.java │ │ │ │ │ └── ForgeUniversalModLoader.java │ │ │ │ ├── ModLoader.java │ │ │ │ ├── ModLoaderManager.java │ │ │ │ └── fabric │ │ │ │ │ └── FabricModLoader.java │ │ │ ├── Profile.java │ │ │ └── StartJson.java │ │ │ ├── migration │ │ │ ├── migrators │ │ │ │ ├── LegacyMigrator.java │ │ │ │ ├── DialogUtil.java │ │ │ │ └── V1To2.java │ │ │ └── Migrator.java │ │ │ ├── IntegrityCheckException.java │ │ │ ├── Settings.java │ │ │ ├── Constants.java │ │ │ └── Instances.java │ └── resources │ │ ├── MigrationMessages.properties │ │ └── log4j2.xml └── test │ └── java │ └── net │ └── creeperhost │ └── creeperlauncher │ └── os │ └── platform │ └── PlatformTests.java ├── .gitlab-ci.yml ├── .gitignore ├── gradlew.bat └── graal.json /README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'creeperlauncher' 2 | include 'web' -------------------------------------------------------------------------------- /DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/DS_Store -------------------------------------------------------------------------------- /ftbl.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/ftbl.icns -------------------------------------------------------------------------------- /ftbl.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/ftbl.ico -------------------------------------------------------------------------------- /ftbl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/ftbl.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /installig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/installig.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "web"] 2 | path = web 3 | url = git@github.com:FTBTeam/FTB-App.git 4 | -------------------------------------------------------------------------------- /lib/i4jruntime.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/lib/i4jruntime.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreeperHost/modpacklauncher/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/SyncInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | public class SyncInstanceData extends InstallInstanceData 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/BaseData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data; 2 | 3 | public class BaseData 4 | { 5 | public String type; 6 | public int requestId; 7 | public String secret; 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/irc/IRCQuitRequestData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class IRCQuitRequestData extends BaseData { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/friends/BlockFriendData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class BlockFriendData extends BaseData { 6 | public String hash; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/install/tasks/http/IProgressUpdater.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.install.tasks.http; 2 | 3 | @FunctionalInterface 4 | public interface IProgressUpdater 5 | { 6 | void update(long currentBytes, long delta, long totalBytes, boolean done); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/irc/IRCSendMessageData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class IRCSendMessageData extends BaseData { 6 | public String nick; 7 | public String message; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/install/tasks/IInstallTask.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.install.tasks; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public interface IInstallTask 6 | { 7 | CompletableFuture execute(); 8 | 9 | Double getProgress(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/MessageClientData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class MessageClientData extends BaseData { 6 | public String uuid; 7 | public String message; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/PingLauncherData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class PingLauncherData extends BaseData { 6 | public PingLauncherData() { 7 | type = "ping"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/PongLauncherData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class PongLauncherData extends BaseData { 6 | public PongLauncherData() { 7 | type = "pong"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/CloseModalData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class CloseModalData extends BaseData { 6 | public CloseModalData() { 7 | type = "closeModal"; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/OSUtils.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | 5 | public class OSUtils 6 | { 7 | 8 | public static String getVersion() 9 | { 10 | return System.getProperty("os.version").toLowerCase(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/friends/AddFriendData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class AddFriendData extends BaseData { 6 | public AddFriendData() { 7 | this.type = "ircAddFriend"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/YeetLauncherData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class YeetLauncherData extends BaseData { 6 | public YeetLauncherData() { 7 | this.type = "yeetLauncher"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/irc/IRCConnectData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class IRCConnectData extends BaseData { 6 | public String host; 7 | public int port; 8 | public String nick; 9 | public String realname; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstanceSetModEnabledData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class InstanceSetModEnabledData extends BaseData { 6 | public String name; 7 | public String uuid; 8 | public boolean state; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/friends/OnlineFriendData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.friends; 2 | 3 | import net.creeperhost.minetogether.lib.chat.data.Profile; 4 | 5 | public class OnlineFriendData extends FriendData { 6 | public OnlineFriendData(Profile profile) { 7 | super(profile); 8 | this.type = "ircOnlineFriend"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/IMessageHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public interface IMessageHandler 6 | { 7 | @SuppressWarnings("unchecked") 8 | default void handle(Object data) 9 | { 10 | handle((T) data); 11 | } 12 | 13 | void handle(T data); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/UpdateInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.instances.InstallInstanceData; 4 | 5 | public class UpdateInstanceHandler extends InstallInstanceHandler 6 | { 7 | @Override 8 | public void handle(InstallInstanceData data) 9 | { 10 | super.handle(data); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | GIT_SUBMODULE_STRATEGY: recursive 3 | 4 | stages: 5 | - build 6 | - publish 7 | 8 | build: 9 | only: 10 | - branches 11 | except: 12 | - tags 13 | - /^no-ci/.*$/ 14 | stage: build 15 | script: 16 | - cd /Users/aaronmills 17 | - ./runBuild.sh 18 | 19 | publish: 20 | only: 21 | - branches 22 | except: 23 | - tags 24 | stage: publish 25 | script: 26 | - ./gradlew publish --debug 27 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/friends/AcceptedFriendData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class AcceptedFriendData extends BaseData { 6 | private final String friendName; 7 | 8 | public AcceptedFriendData(String friendName) { 9 | this.type = "ircAcceptedFriend"; 10 | this.friendName = friendName; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/irc/IRCPartyInviteData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.friends.FriendData; 4 | import net.creeperhost.minetogether.lib.chat.data.Profile; 5 | 6 | public class IRCPartyInviteData extends FriendData { 7 | public IRCPartyInviteData(Profile profile) { 8 | super(profile); 9 | this.type = "ircPartyInvite"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/friends/AddFriendHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.friends.AddFriendData; 4 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 5 | 6 | public class AddFriendHandler implements IMessageHandler { 7 | @Override 8 | public void handle(AddFriendData data) { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/install/tasks/http/IHttpClient.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.install.tasks.http; 2 | 3 | import com.google.common.hash.HashFunction; 4 | 5 | import java.nio.file.Path; 6 | 7 | public interface IHttpClient 8 | { 9 | String makeRequest(String url); 10 | 11 | public DownloadedFile doDownload(String url, Path destination, IProgressUpdater progressWatcher, HashFunction hashFunc, long maxSpeed) throws Throwable; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/friends/FriendData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | import net.creeperhost.minetogether.lib.chat.data.Profile; 5 | 6 | public abstract class FriendData extends BaseData { 7 | Profile profile; 8 | public FriendData(Profile profile){ 9 | this.profile = profile; 10 | type = "ircFriendProfile"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/StoreAuthDetailsData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class StoreAuthDetailsData extends BaseData 6 | { 7 | public String mpKey; 8 | public String mpSecret; 9 | public String mtHash; 10 | public String s3Key; 11 | public String s3Secret; 12 | public String s3Bucket; 13 | public String s3Host; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/Pair.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | public class Pair 4 | { 5 | private final L left; 6 | private final R right; 7 | 8 | public Pair(L left, R right) 9 | { 10 | this.left = left; 11 | this.right = right; 12 | } 13 | 14 | public L getLeft() 15 | { 16 | return left; 17 | } 18 | 19 | public R getRight() 20 | { 21 | return right; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/irc/IRCEventMessageData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class IRCEventMessageData extends BaseData { 6 | private final String message; 7 | private final String nick; 8 | 9 | public IRCEventMessageData(String message, String nick) { 10 | this.type = "ircMessage"; 11 | this.message = message; 12 | this.nick = nick; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/window/IMonitor.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform.window; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * Represents a monitor. 7 | */ 8 | public interface IMonitor { 9 | 10 | /** 11 | * Gets this monitors index. 12 | * 13 | * @return The index. 14 | */ 15 | int getNumber(); 16 | 17 | /** 18 | * Gets the size of this monitor. 19 | * 20 | * @return The size. 21 | */ 22 | Rectangle getBounds(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/PlatformNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os; 2 | 3 | /** 4 | * May be thrown when an operation was performed that is currently not supported on this platform. 5 | * These errors are almost always fatal. 6 | *

7 | * Created by covers1624 on 12/2/21. 8 | */ 9 | public class PlatformNotSupportedException extends RuntimeException { 10 | 11 | public PlatformNotSupportedException() { 12 | super(System.getProperty("os.name")); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/YeetLauncherHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.CreeperLauncher; 4 | import net.creeperhost.creeperlauncher.api.data.other.YeetLauncherData; 5 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 6 | 7 | public class YeetLauncherHandler implements IMessageHandler { 8 | @Override 9 | public void handle(YeetLauncherData data) { 10 | CreeperLauncher.exit(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/GetJavasData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | import java.util.HashMap; 6 | 7 | public class GetJavasData extends BaseData { 8 | public static class Reply extends BaseData { 9 | HashMap javas; 10 | public Reply(GetJavasData data, HashMap javas) { 11 | this.javas = javas; 12 | this.requestId = data.requestId; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/irc/IRCQuitRequestHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.irc.IRCQuitRequestData; 4 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 5 | import net.creeperhost.minetogether.lib.chat.irc.IrcHandler; 6 | 7 | public class IRCQuitRequestHandler implements IMessageHandler { 8 | @Override 9 | public void handle(IRCQuitRequestData data) { 10 | IrcHandler.stop(false); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/PongLauncherHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.CreeperLauncher; 4 | import net.creeperhost.creeperlauncher.api.data.other.PongLauncherData; 5 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 6 | 7 | public class PongLauncherHandler implements IMessageHandler { 8 | @Override 9 | public void handle(PongLauncherData data) { 10 | CreeperLauncher.missedPings = 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/pack/IPack.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.pack; 2 | 3 | import java.nio.file.Path; 4 | import java.util.List; 5 | 6 | public interface IPack 7 | { 8 | long getId(); 9 | 10 | String getName(); 11 | 12 | String getVersion(); 13 | 14 | Path getDir(); 15 | 16 | List getAuthors(); 17 | 18 | String getDescription(); 19 | 20 | String getMcVersion(); 21 | 22 | String getUrl(); 23 | 24 | String getArtURL(); 25 | 26 | int getMinMemory(); 27 | 28 | int getRecMemory(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/irc/IRCSendMessageHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.irc; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.irc.IRCSendMessageData; 4 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 5 | import net.creeperhost.minetogether.lib.chat.ChatHandler; 6 | 7 | public class IRCSendMessageHandler implements IMessageHandler { 8 | @Override 9 | public void handle(IRCSendMessageData data) { 10 | ChatHandler.sendMessage(data.nick, data.message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/MigrationMessages.properties: -------------------------------------------------------------------------------- 1 | migration.newer_format=You are attempting to load the FTBApp after a newer version has already modified files.\n\ 2 | Your existing Instances may not work, and could potentially suffer data loss if you continue.\n\ 3 | \n\n\ 4 | Do you wish to continue? 5 | migration.required=The FTBApp needs to migrate some data before it can be used.\n\ 6 | This may take several minutes to complete.\n\ 7 | Would you like to continue? 8 | migration.error=Fatal exception occurred whilst performing data migration. Please provide the attached logs to support. The FTBApp will now exit.
9 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/BrowseInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | 4 | import net.creeperhost.creeperlauncher.api.data.BaseData; 5 | 6 | public class BrowseInstanceData extends BaseData 7 | { 8 | public String uuid; 9 | 10 | public static class Reply extends BaseData 11 | { 12 | String status; 13 | 14 | public Reply(BrowseInstanceData data, String status) 15 | { 16 | type = "browseInstanceReply"; 17 | requestId = data.requestId; 18 | this.status = status; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/InstalledFileEventData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import net.creeperhost.creeperlauncher.api.data.BaseData; 5 | 6 | import java.util.HashMap; 7 | 8 | public class InstalledFileEventData extends BaseData { 9 | public static class Reply extends BaseData { 10 | ConcurrentHashMap files; 11 | public Reply(ConcurrentHashMap files) { 12 | this.files = files; 13 | this.type = "installedFileEventDataReply"; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.gradle 3 | /build 4 | /out 5 | /creepernatives 6 | /node_modules 7 | /wix 8 | /jdk 9 | /instances 10 | /bin 11 | /jre 12 | /releaseBuild 13 | /releaseUpload 14 | /natives 15 | *.class 16 | wix.zip 17 | jpackager.archive 18 | package-lock.json 19 | creeperlauncher.log* 20 | hs_err* 21 | debug.log* 22 | jre.archive 23 | rss.xml 24 | screenlog* 25 | /GPUCache 26 | /.localCache 27 | launcher.log.lck 28 | 29 | launcher.log 30 | 31 | /test 32 | 33 | .DS_Store 34 | 35 | src/main/java/net/creeperhost/creeperlauncher/VersionTest.java 36 | 37 | launcher.log.1 38 | 39 | launcher.log.1.lck 40 | 41 | tempDMGExtract/ 42 | 43 | electron.log 44 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/GetJavasHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.CreeperLauncher; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.api.data.other.GetJavasData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | 8 | public class GetJavasHandler implements IMessageHandler { 9 | @Override 10 | public void handle(GetJavasData data) { 11 | Settings.webSocketAPI.sendMessage(new GetJavasData.Reply(data, CreeperLauncher.javaVersions)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/FileHashData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class FileHashData extends BaseData { 6 | public String uuid; 7 | public String filePath; 8 | 9 | public static class Reply extends BaseData { 10 | public String md5Hash; 11 | public String shaHash; 12 | public Reply(FileHashData data, String md5Hash, String shaHash) 13 | { 14 | requestId = data.requestId; 15 | this.md5Hash = md5Hash; 16 | this.shaHash = shaHash; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/friends/GetFriendsData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | import net.creeperhost.minetogether.lib.chat.data.Profile; 5 | 6 | import java.util.List; 7 | 8 | public class GetFriendsData extends BaseData { 9 | 10 | public static class Reply extends BaseData { 11 | List online; 12 | List offline; 13 | Reply(List online, List offline) { 14 | this.type = "ircFriendsReply"; 15 | this.online = online; 16 | this.offline = offline; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/window/win/WindowsMonitor.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform.window.win; 2 | 3 | import net.creeperhost.creeperlauncher.os.platform.window.IMonitor; 4 | 5 | import java.awt.*; 6 | 7 | public class WindowsMonitor implements IMonitor { 8 | 9 | private final int number; 10 | private final Rectangle rect; 11 | 12 | public WindowsMonitor(int i, Rectangle rect) { 13 | this.number = i; 14 | this.rect = rect; 15 | } 16 | 17 | @Override 18 | public int getNumber() { 19 | return number; 20 | } 21 | 22 | @Override 23 | public Rectangle getBounds() { 24 | return rect; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/LaunchInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | 4 | import net.creeperhost.creeperlauncher.api.data.BaseData; 5 | 6 | public class LaunchInstanceData extends BaseData 7 | { 8 | public String uuid; 9 | public String extraArgs = ""; 10 | public boolean loadInApp = true; 11 | 12 | public static class Reply extends BaseData 13 | { 14 | String status; 15 | 16 | public Reply(LaunchInstanceData data, String status) 17 | { 18 | type = "launchInstanceReply"; 19 | requestId = data.requestId; 20 | this.status = status; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/SettingsInfoHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.api.data.other.SettingsInfoData; 5 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 6 | 7 | import java.util.HashMap; 8 | 9 | public class SettingsInfoHandler implements IMessageHandler 10 | { 11 | @Override 12 | public void handle(SettingsInfoData data) 13 | { 14 | HashMap settingsInfo = Settings.settings; 15 | Settings.webSocketAPI.sendMessage(new SettingsInfoData.Reply(data, settingsInfo)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/UninstallInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class UninstallInstanceData extends BaseData 6 | { 7 | public String uuid; 8 | 9 | public static class Reply extends BaseData 10 | { 11 | final String status; 12 | final String message; 13 | 14 | public Reply(UninstallInstanceData data, String status, String message) 15 | { 16 | type = "uninstallInstanceDataReply"; 17 | requestId = data.requestId; 18 | this.status = status; 19 | this.message = message; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/SettingsConfigureData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | import java.util.HashMap; 6 | 7 | public class SettingsConfigureData extends BaseData 8 | { 9 | 10 | public HashMap settingsInfo; //TODO: second parameter should be something other than String maybe 11 | 12 | public static class Reply extends BaseData 13 | { 14 | String status; 15 | 16 | public Reply(SettingsConfigureData data, String status) 17 | { 18 | type = "saveSettingsReply"; 19 | this.requestId = data.requestId; 20 | this.status = "success"; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/friends/GetFriendsHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.friends; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.friends.GetFriendsData; 4 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 5 | import net.creeperhost.minetogether.lib.chat.KnownUsers; 6 | import net.creeperhost.minetogether.lib.chat.data.Profile; 7 | 8 | import java.util.stream.Collectors; 9 | 10 | public class GetFriendsHandler implements IMessageHandler { 11 | @Override 12 | public void handle(GetFriendsData data) { 13 | KnownUsers.getFriends().stream().filter(Profile::isOnline).collect(Collectors.toList()); 14 | KnownUsers.getFriends().stream().filter((p) -> !p.isOnline()).collect(Collectors.toList()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstanceModsData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | import net.creeperhost.creeperlauncher.api.handlers.ModFile; 5 | 6 | import java.util.List; 7 | 8 | public class InstanceModsData extends BaseData 9 | { 10 | public String uuid; 11 | public boolean _private = false; 12 | public static class Reply extends InstanceModsData 13 | { 14 | public List files; 15 | 16 | public Reply(InstanceModsData data, List simpleDownloadableFiles) 17 | { 18 | type = "instanceModsReply"; 19 | uuid = data.uuid; 20 | requestId = data.requestId; 21 | this.files = simpleDownloadableFiles; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstanceConfigureData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | import java.util.HashMap; 6 | 7 | public class InstanceConfigureData extends BaseData 8 | { 9 | public String uuid; 10 | public HashMap instanceInfo; //TODO: second parameter should be something other than String maybe 11 | 12 | public static class Reply extends BaseData 13 | { 14 | String uuid; 15 | String status; 16 | 17 | public Reply(InstanceConfigureData data, String status) 18 | { 19 | type = "instanceConfigureReply"; 20 | this.requestId = data.requestId; 21 | this.uuid = data.uuid; 22 | this.status = "success"; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/ShareInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class ShareInstanceData extends BaseData 6 | { 7 | public String uuid; 8 | 9 | public static class Reply extends BaseData 10 | { 11 | final String status; 12 | final String message; 13 | final String uuid; 14 | final String code; 15 | 16 | public Reply(ShareInstanceData data, String status, String message, String uuid, String code) 17 | { 18 | type = "ShareInstanceDataReply"; 19 | requestId = data.requestId; 20 | this.status = status; 21 | this.message = message; 22 | this.uuid = uuid; 23 | this.code = code; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/UploadLogsData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class UploadLogsData extends BaseData { 6 | public String frontendLogs; 7 | public String uiVersion; 8 | public UploadLogsData() 9 | { 10 | type = "uploadLogs"; 11 | } 12 | 13 | public static class Reply extends BaseData { 14 | private final boolean error; 15 | private String code; 16 | public Reply(int requestId, String code) { 17 | this.requestId = requestId; 18 | this.code = code; 19 | this.type = "uploadLogsReply"; 20 | this.error = false; 21 | } 22 | 23 | public Reply(int requestId) { 24 | this.requestId = requestId; 25 | this.error = true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/CancelInstallInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class CancelInstallInstanceData extends BaseData 6 | { 7 | public String uuid; 8 | 9 | public static class Reply extends BaseData 10 | { 11 | final String status; 12 | final String message; 13 | final String uuid; 14 | 15 | public Reply(CancelInstallInstanceData data, String status, String message, String uuid) 16 | { 17 | type = "installInstanceDataReply"; 18 | requestId = data.requestId; 19 | this.status = status; 20 | this.message = message; 21 | this.uuid = uuid; 22 | // Todo: get tasks from install, update corresponding TaskData objects and send update to other side 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstalledInstancesData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.creeperhost.creeperlauncher.api.data.BaseData; 5 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 6 | 7 | import java.util.List; 8 | 9 | public class InstalledInstancesData extends BaseData 10 | { 11 | public boolean refresh = false; 12 | public static class Reply extends BaseData 13 | { 14 | List instances; 15 | List cloudInstances; 16 | 17 | public Reply(int requestId, List instances, List cloudInstances) 18 | { 19 | this.instances = instances; 20 | this.type = "installedInstancesReply"; 21 | this.requestId = requestId; 22 | this.cloudInstances = cloudInstances; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/CancelInstallInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.CreeperLauncher; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.api.data.instances.CancelInstallInstanceData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | 8 | public class CancelInstallInstanceHandler implements IMessageHandler 9 | { 10 | @Override 11 | public void handle(CancelInstallInstanceData data) 12 | { 13 | if (CreeperLauncher.isInstalling.get()) 14 | { 15 | CreeperLauncher.currentInstall.get().cancel(); 16 | Settings.webSocketAPI.sendMessage(new CancelInstallInstanceData.Reply(data, "success", "Cancelled Install", CreeperLauncher.currentInstall.get().currentUUID)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/ElapsedTimer.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * Created by covers1624 on 13/1/21. 7 | */ 8 | public class ElapsedTimer { 9 | 10 | private final long start; 11 | 12 | public ElapsedTimer() { 13 | start = System.nanoTime(); 14 | } 15 | 16 | public long elapsedNanos() { 17 | return System.nanoTime() - start; 18 | } 19 | 20 | public String elapsedStr() { 21 | return timeString(elapsedNanos()); 22 | } 23 | 24 | //TODO, this needs to support Seconds. 25 | public static String timeString(long delta) { 26 | long millis = TimeUnit.NANOSECONDS.toMillis(delta); 27 | 28 | String s; 29 | if (millis >= 5) { 30 | s = millis + "ms(" + delta + "ns)"; 31 | } else { 32 | s = delta + "ns"; 33 | } 34 | return s; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/Analytics.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher; 2 | 3 | import net.creeperhost.creeperlauncher.util.WebUtils; 4 | import java.util.concurrent.CompletableFuture; 5 | 6 | public class Analytics 7 | { 8 | public static void sendInstallRequest(long packID, long packVersion, byte packType) 9 | { 10 | String analytics = Constants.getCreeperhostModpackSearch2(false, packType) + "/" + packID + "/" + packVersion + "/install"; 11 | CompletableFuture.runAsync(() -> { 12 | WebUtils.getAPIResponse(analytics); 13 | }); 14 | } 15 | 16 | public static void sendPlayRequest(long packID, long packVersion, byte packType) 17 | { 18 | String analytics = Constants.getCreeperhostModpackSearch2(false, packType) + "/" + packID + "/" + packVersion + "/play"; 19 | CompletableFuture.runAsync(() -> { 20 | WebUtils.getAPIResponse(analytics); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/InstanceSetModEnabledHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Instances; 4 | import net.creeperhost.creeperlauncher.api.data.instances.InstanceSetModEnabledData; 5 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 6 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 7 | 8 | import java.util.UUID; 9 | import java.util.stream.Collectors; 10 | 11 | public class InstanceSetModEnabledHandler implements IMessageHandler { 12 | @Override 13 | public void handle(InstanceSetModEnabledData data) { 14 | LocalInstance instance = Instances.getInstance(UUID.fromString(data.uuid)); 15 | boolean result = instance.getMods().stream().filter(mod -> mod.getName().equals(data.name)).limit(1).map(mod -> mod.setEnabled(data.state)).collect(Collectors.toList()).get(0); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/modloader/forge/ForgeModLoader.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft.modloader.forge; 2 | 3 | import net.creeperhost.creeperlauncher.minecraft.modloader.ModLoader; 4 | import net.creeperhost.creeperlauncher.util.ForgeUtils; 5 | import net.creeperhost.creeperlauncher.util.LoaderTarget; 6 | 7 | import java.util.List; 8 | 9 | public abstract class ForgeModLoader extends ModLoader 10 | { 11 | public ForgeModLoader(List loaderTargets) 12 | { 13 | super(loaderTargets); 14 | } 15 | 16 | public String getForgeVersion() 17 | { 18 | String version = getTargetVersion("forge").orElseThrow(); 19 | if (version.contains("recommended")) 20 | { 21 | version = ForgeUtils.getRecommended(getMinecraftVersion()); 22 | return version; 23 | } 24 | else if (version.contains("latest")) 25 | { 26 | version = ForgeUtils.getLatest(getMinecraftVersion()); 27 | return version; 28 | } 29 | return version; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/LaunchInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.Instances; 5 | import net.creeperhost.creeperlauncher.api.data.instances.LaunchInstanceData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 8 | 9 | import java.util.UUID; 10 | 11 | public class LaunchInstanceHandler implements IMessageHandler 12 | { 13 | @Override 14 | public void handle(LaunchInstanceData data) 15 | { 16 | String _uuid = data.uuid; 17 | UUID uuid = UUID.fromString(_uuid); 18 | LocalInstance instance = Instances.getInstance(uuid); 19 | instance.play(data.extraArgs, data.loadInApp); 20 | Settings.webSocketAPI.sendMessage(new LaunchInstanceData.Reply(data, "success")); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/ModalCallbackHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.other.OpenModalData; 4 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 5 | 6 | public class ModalCallbackHandler implements IMessageHandler { 7 | @Override 8 | public void handle(OpenModalData.ModalCallbackData data) { 9 | String id = data.id; 10 | String message = data.message; 11 | if (OpenModalData.currentlyOpenModal != null && OpenModalData.currentlyOpenModal.id.equals(id)) 12 | { 13 | for(OpenModalData.ModalButton button : OpenModalData.currentlyOpenModal.buttons) 14 | { 15 | if (button.message.equals(message)) 16 | { 17 | button.callback.run(); 18 | break; 19 | } 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/UpdateInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.install.tasks.FTBModPackInstallerTask; 4 | 5 | public class UpdateInstanceData extends InstallInstanceData 6 | { 7 | public static String typePrefix = "update"; 8 | 9 | public static class Reply extends InstallInstanceData.Reply 10 | { 11 | 12 | public Reply(InstallInstanceData data, String status, String message, String uuid) 13 | { 14 | super(data, status, message, uuid); 15 | } 16 | } 17 | 18 | public static class Progress extends InstallInstanceData.Progress 19 | { 20 | 21 | public Progress(InstallInstanceData data, Double overallPercentage, long speed, long currentBytes, long overallBytes, FTBModPackInstallerTask.Stage currentStage) 22 | { 23 | super(data, overallPercentage, speed, currentBytes, overallBytes, currentStage); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/install/tasks/http/DownloadedFile.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.install.tasks.http; 2 | 3 | import com.google.common.hash.HashCode; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.nio.file.Path; 8 | 9 | @SuppressWarnings ("UnstableApiUsage") 10 | public class DownloadedFile 11 | { 12 | @NotNull 13 | private final Path destination; 14 | private final long size; 15 | @Nullable 16 | private final HashCode checksum; 17 | 18 | public DownloadedFile(Path destination, long size, HashCode checksum) 19 | { 20 | this.destination = destination; 21 | this.size = size; 22 | this.checksum = checksum; 23 | } 24 | 25 | public Path getDestination() 26 | { 27 | return destination; 28 | } 29 | 30 | public long getSize() 31 | { 32 | return size; 33 | } 34 | 35 | public HashCode getChecksum() 36 | { 37 | return checksum; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/instanceEvent.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public class instanceEvent { 6 | private boolean async = true; 7 | private Runnable code; 8 | private String name = "anonymous"; 9 | public instanceEvent(Runnable lambda) 10 | { 11 | code = lambda; 12 | } 13 | public instanceEvent(Runnable lambda, String name) 14 | { 15 | this.name = name; 16 | code = lambda; 17 | } 18 | public instanceEvent(Runnable lambda, boolean blocking) 19 | { 20 | async = !blocking; 21 | code = lambda; 22 | } 23 | public instanceEvent(Runnable lambda, String name, boolean blocking) 24 | { 25 | this.name = name; 26 | async = !blocking; 27 | code = lambda; 28 | } 29 | public CompletableFuture Run() 30 | { 31 | CompletableFuture ft = CompletableFuture.runAsync(code); 32 | if(!async) ft.join(); 33 | return ft; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/UploadLogsHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.api.data.other.UploadLogsData; 5 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 6 | import net.creeperhost.creeperlauncher.util.LogsUploader; 7 | 8 | public class UploadLogsHandler implements IMessageHandler { 9 | 10 | @Override 11 | public void handle(UploadLogsData data) { 12 | uploadLogs(data.uiVersion, data.frontendLogs, data.requestId); 13 | } 14 | 15 | public static void uploadLogs(String uiVersion, String frontendLogs, int requestId) { 16 | String code = LogsUploader.uploadUILogs(uiVersion, frontendLogs); 17 | if (code == null) { 18 | Settings.webSocketAPI.sendMessage(new UploadLogsData.Reply(requestId)); // error 19 | } 20 | Settings.webSocketAPI.sendMessage(new UploadLogsData.Reply(requestId, code)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/StreamGobblerLog.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.concurrent.CompletableFuture; 8 | import java.util.function.Consumer; 9 | 10 | public class StreamGobblerLog { 11 | public static CompletableFuture redirectToLogger(final InputStream inputStream, final Consumer logLineConsumer) { 12 | return CompletableFuture.runAsync(() -> { 13 | try ( 14 | InputStreamReader inputStreamReader = new InputStreamReader(inputStream); 15 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 16 | ) { 17 | String line; 18 | while((line = bufferedReader.readLine()) != null) { 19 | logLineConsumer.accept(line); 20 | } 21 | } catch (IOException ignored) { 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/net/creeperhost/creeperlauncher/os/platform/PlatformTests.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.List; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | /** 11 | * Created by covers1624 on 8/3/21. 12 | */ 13 | public class PlatformTests { 14 | 15 | @Test 16 | public void testMacosPlatformLauncherArgs() { 17 | //Test that our Macos launcher process args are correct. 18 | MacosPlatform platform = new MacosPlatform(); 19 | List args = platform.prepareLauncherProcessArgs(); 20 | assertEquals(5, args.size()); 21 | assertEquals("/usr/bin/open", args.get(0)); 22 | assertEquals(platform.getLauncherOpenPath().toAbsolutePath().toString(), args.get(1)); 23 | assertEquals("--args", args.get(2)); 24 | assertEquals("--workDir", args.get(3)); 25 | assertEquals(Constants.BIN_LOCATION.toAbsolutePath().toString(), args.get(4)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/UnixPlatform.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.creeperhost.creeperlauncher.os.platform.window.IWindowHelper; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.attribute.PosixFilePermissions; 9 | 10 | /** 11 | * Abstract class for Unix-like Platforms. 12 | *

13 | * Created by covers1624 on 9/2/21. 14 | */ 15 | public abstract class UnixPlatform extends BasePlatform { 16 | 17 | protected UnixPlatform(IWindowHelper windowHelper) { 18 | super(windowHelper); 19 | } 20 | 21 | @Override 22 | protected void prepareLauncherEnvironment(ProcessBuilder builder) { 23 | super.prepareLauncherEnvironment(builder); 24 | //Thanks jikuja :D 25 | builder.environment().put("LC_ALL", "en_US.UTF-8"); 26 | } 27 | 28 | public static void chmod755(Path path) throws IOException { 29 | Files.setPosixFilePermissions(path, PosixFilePermissions.fromString("rwxr-xr-x")); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/ClientLaunchData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | 5 | public class ClientLaunchData extends BaseData { 6 | public static class Reply extends BaseData { 7 | String messageType; 8 | String message; 9 | String instance; 10 | Object clientData; 11 | public Reply(String instance, String messageType, String message){ 12 | this(instance, messageType, message, null); 13 | } 14 | public Reply(String instance, String messageType, Object clientData){ 15 | this(instance, messageType, null, clientData); 16 | } 17 | public Reply(String instance, String messageType, String message, Object clientData) 18 | { 19 | this.messageType = messageType; 20 | this.message = message; 21 | this.clientData = clientData; 22 | this.instance = instance; 23 | this.type = "clientLaunchData"; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/ImageUtils.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import javax.imageio.ImageIO; 4 | import java.awt.*; 5 | import java.awt.image.BufferedImage; 6 | import java.io.ByteArrayInputStream; 7 | import java.io.IOException; 8 | 9 | public class ImageUtils { 10 | public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException { 11 | BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB); 12 | Graphics2D graphics2D = resizedImage.createGraphics(); 13 | graphics2D.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null); 14 | graphics2D.dispose(); 15 | return resizedImage; 16 | } 17 | public static BufferedImage resizeImage(byte[] originalImage, int targetWidth, int targetHeight) throws IOException { 18 | ByteArrayInputStream bais = new ByteArrayInputStream(originalImage); 19 | BufferedImage bi = ImageIO.read(bais); 20 | return resizeImage(bi, targetWidth, targetHeight); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/UnknownPlatform.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.creeperhost.creeperlauncher.os.Platform; 4 | import net.creeperhost.creeperlauncher.os.platform.window.IWindowHelper; 5 | 6 | import java.nio.file.Path; 7 | 8 | /** 9 | * Represents an unsupported Platform. 10 | * All operations will result in an {@link UnsupportedOperationException} being thrown. 11 | *

12 | * Created by covers1624 on 9/2/21. 13 | */ 14 | public class UnknownPlatform implements Platform { 15 | 16 | //@formatter:off 17 | @Override public IWindowHelper getWindowHelper() { throw new UnsupportedOperationException(); } 18 | @Override public String getLauncherURL() { throw new UnsupportedOperationException(); } 19 | @Override public Path getLauncherExecutable() { throw new UnsupportedOperationException(); } 20 | @Override public void unpackLauncher(Path downloadedLauncher) { throw new UnsupportedOperationException(); } 21 | @Override public boolean installLauncher() { throw new UnsupportedOperationException(); } 22 | @Override public ProcessBuilder buildLauncherProcess() { throw new UnsupportedOperationException(); } 23 | //@formatter:on 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/SettingsChangeUtil.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import java.util.HashMap; 7 | 8 | public class SettingsChangeUtil { 9 | private static final Logger LOGGER = LogManager.getLogger(); 10 | 11 | private static HashMap settingsChangedListeners = new HashMap<>(); 12 | 13 | public static void registerListener(String key, ISettingsChangedHandler handler) { 14 | if (settingsChangedListeners.containsKey(key)) { 15 | LOGGER.warn("New handler for {} when already exists, doing nothing", key); 16 | return; 17 | } 18 | settingsChangedListeners.put(key, handler); 19 | } 20 | 21 | public static boolean settingsChanged(String key, String value) { 22 | if (settingsChangedListeners.containsKey(key)) { 23 | try { 24 | return settingsChangedListeners.get(key).handle(key, value); 25 | } catch (Exception e) { 26 | } 27 | } 28 | return true; 29 | } 30 | 31 | public interface ISettingsChangedHandler { 32 | boolean handle(String key, String value); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/BrowseInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.Instances; 5 | import net.creeperhost.creeperlauncher.api.data.instances.BrowseInstanceData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 8 | 9 | import java.util.UUID; 10 | 11 | public class BrowseInstanceHandler implements IMessageHandler 12 | { 13 | @Override 14 | public void handle(BrowseInstanceData data) 15 | { 16 | try 17 | { 18 | LocalInstance instance = Instances.getInstance(UUID.fromString(data.uuid)); 19 | if (instance.browse()) 20 | { 21 | Settings.webSocketAPI.sendMessage(new BrowseInstanceData.Reply(data, "success")); 22 | } else 23 | { 24 | Settings.webSocketAPI.sendMessage(new BrowseInstanceData.Reply(data, "error")); 25 | } 26 | } catch (Exception err) 27 | { 28 | Settings.webSocketAPI.sendMessage(new BrowseInstanceData.Reply(data, "error")); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/MessageClientHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.creeperhost.creeperlauncher.CreeperLauncher; 5 | import net.creeperhost.creeperlauncher.api.data.instances.MessageClientData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | public class MessageClientHandler implements IMessageHandler { 11 | private static final Logger LOGGER = LogManager.getLogger(); 12 | 13 | @Override 14 | public void handle(MessageClientData data) { 15 | try { 16 | if (CreeperLauncher.socket != null && CreeperLauncher.socket.isConnected()) 17 | { 18 | JsonObject jsonObject = new JsonObject(); 19 | jsonObject.addProperty("instance", data.uuid); 20 | jsonObject.addProperty("message", data.message); 21 | CreeperLauncher.socketWrite.write((jsonObject.toString()+"\n").getBytes()); 22 | } 23 | } catch (Throwable e) { 24 | LOGGER.warn("Error sending message to Minecraft client", e); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/WindowsPlatform.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.os.platform.window.win.WindowsWindowHelper; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.StandardCopyOption; 10 | 11 | /** 12 | * Represents the Windows Platform. 13 | *

14 | * Created by covers1624 on 9/2/21. 15 | */ 16 | public class WindowsPlatform extends BasePlatform { 17 | 18 | private static final String LAUNCHER_URL = "https://launcher.mojang.com/download/Minecraft.exe"; 19 | private static final String LAUNCHER_EXECUTABLE = "launcher.exe"; 20 | 21 | public WindowsPlatform() { 22 | super(new WindowsWindowHelper()); 23 | } 24 | 25 | @Override 26 | public String getLauncherURL() { 27 | return LAUNCHER_URL; 28 | } 29 | 30 | @Override 31 | public Path getLauncherExecutable() { 32 | return Constants.BIN_LOCATION.resolve(LAUNCHER_EXECUTABLE); 33 | } 34 | 35 | @Override 36 | public void unpackLauncher(Path downloadedLauncher) throws IOException { 37 | Files.copy(downloadedLauncher, getLauncherExecutable(), StandardCopyOption.REPLACE_EXISTING); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/migration/migrators/LegacyMigrator.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.migration.migrators; 2 | 3 | import com.google.gson.reflect.TypeToken; 4 | import net.creeperhost.creeperlauncher.Constants; 5 | import net.creeperhost.creeperlauncher.migration.MigrationContext; 6 | import net.creeperhost.creeperlauncher.migration.Migrator; 7 | import net.creeperhost.creeperlauncher.os.OS; 8 | import net.creeperhost.creeperlauncher.util.FileUtils; 9 | import net.creeperhost.creeperlauncher.util.GsonUtils; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | import java.io.IOException; 14 | import java.lang.reflect.Type; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Handles moving FTBApp data from ~/.ftba on Windows and Mac. 22 | *

23 | * Created by covers1624 on 13/1/21. 24 | */ 25 | @Migrator.Properties (from = -1, to = 1) 26 | public class LegacyMigrator implements Migrator { 27 | 28 | private static final Logger LOGGER = LogManager.getLogger(); 29 | 30 | private static final Type settingsToken = new TypeToken>() {}.getType(); 31 | 32 | @Override 33 | public void operate(MigrationContext ctx) throws MigrationException { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/friends/BlockFriendHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.friends; 2 | 3 | import com.google.gson.reflect.TypeToken; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.api.data.friends.BlockFriendData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import net.creeperhost.creeperlauncher.util.GsonUtils; 8 | 9 | import java.lang.reflect.Type; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class BlockFriendHandler implements IMessageHandler { 14 | @Override 15 | public void handle(BlockFriendData data) { 16 | List blockedUsers; 17 | if(Settings.settings.containsKey("blockedUsers")){ 18 | String blockedString = Settings.settings.get("blockedUsers"); 19 | Type listOfString = new TypeToken>() {}.getType(); 20 | blockedUsers = GsonUtils.GSON.fromJson(blockedString, listOfString); 21 | } else { 22 | blockedUsers = new ArrayList<>(); 23 | } 24 | if(blockedUsers.contains(data.hash)){ 25 | return; 26 | } 27 | blockedUsers.add(data.hash); 28 | Settings.settings.put("blockedUsers", GsonUtils.GSON.toJson(blockedUsers)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/SimpleDownloadableFile.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api; 2 | 3 | import java.nio.file.Path; 4 | 5 | public class SimpleDownloadableFile 6 | { 7 | String version; 8 | Path path; 9 | long size; 10 | boolean clientSide; 11 | boolean optional; 12 | long id; 13 | String name; 14 | String type; 15 | String sha1; 16 | 17 | public SimpleDownloadableFile(String version, Path path, long size, boolean clientSide, boolean optional, long id, String name, String type) 18 | { 19 | this.version = version; 20 | this.path = path; 21 | this.size = size; 22 | this.clientSide = clientSide; 23 | this.optional = optional; 24 | this.id = id; 25 | this.name = name; 26 | this.type = type; 27 | } 28 | 29 | public String getVersion() 30 | { 31 | return version; 32 | } 33 | 34 | public Path getPath() 35 | { 36 | return path; 37 | } 38 | 39 | public String getSha1() 40 | { 41 | return sha1; 42 | } 43 | 44 | public long getId() 45 | { 46 | return id; 47 | } 48 | 49 | public String getName() 50 | { 51 | return name; 52 | } 53 | 54 | public String getType() 55 | { 56 | return type; 57 | } 58 | 59 | public long getSize() 60 | { 61 | return size; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/UninstallInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.api.data.instances.UninstallInstanceData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | public class UninstallInstanceHandler implements IMessageHandler 12 | { 13 | private static final Logger LOGGER = LogManager.getLogger(); 14 | 15 | @Override 16 | public void handle(UninstallInstanceData data) 17 | { 18 | try 19 | { 20 | //TODO, Instance lookup? 21 | LocalInstance instance = new LocalInstance(Settings.getInstanceLocOr(Constants.INSTANCES_FOLDER_LOC).resolve(data.uuid)); 22 | instance.uninstall(); 23 | Settings.webSocketAPI.sendMessage(new UninstallInstanceData.Reply(data, "success", "")); 24 | } catch (Exception err) 25 | { 26 | LOGGER.error("Error uninstalling pack", err); 27 | Settings.webSocketAPI.sendMessage(new UninstallInstanceData.Reply(data, "error", err.toString())); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/LoaderTarget.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | public class LoaderTarget 4 | { 5 | String name; 6 | long id; 7 | String type; 8 | String version; 9 | 10 | public LoaderTarget(String name, String version, long id, String type) 11 | { 12 | this.name = name; 13 | this.version = version; 14 | this.id = id; 15 | this.type = type; 16 | } 17 | 18 | public String getType() 19 | { 20 | return type; 21 | } 22 | 23 | public String getName() 24 | { 25 | return name; 26 | } 27 | 28 | public long getId() 29 | { 30 | return id; 31 | } 32 | 33 | public String getVersion() 34 | { 35 | return version; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) { 40 | if(obj == this) { 41 | return true; 42 | } 43 | 44 | if (obj instanceof LoaderTarget) { 45 | LoaderTarget otherTarget = (LoaderTarget) obj; 46 | return otherTarget.version.equals(this.version) && 47 | otherTarget.name.equals(this.name) && 48 | otherTarget.type.equals(this.type); 49 | } 50 | 51 | return false; 52 | } 53 | 54 | public boolean equalsNoVersion(LoaderTarget otherTarget) { 55 | return otherTarget.name.equals(this.name) && 56 | otherTarget.type.equals(this.type); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/FileHashHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import com.google.common.hash.HashFunction; 4 | import com.google.common.hash.Hashing; 5 | import net.covers1624.quack.util.HashUtils; 6 | import net.creeperhost.creeperlauncher.Constants; 7 | import net.creeperhost.creeperlauncher.Settings; 8 | import net.creeperhost.creeperlauncher.api.data.other.FileHashData; 9 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | import java.io.IOException; 14 | import java.nio.file.Path; 15 | 16 | public class FileHashHandler implements IMessageHandler { 17 | private static final Logger LOGGER = LogManager.getLogger(); 18 | 19 | @Override 20 | public void handle(FileHashData data) { 21 | Path file = Settings.getInstanceLocOr(Constants.INSTANCES_FOLDER_LOC).resolve(data.uuid).resolve(data.filePath); 22 | Settings.webSocketAPI.sendMessage(new FileHashData.Reply(data, safeHash(Hashing.md5(), file), safeHash(Hashing.sha256(), file))); 23 | } 24 | 25 | private static String safeHash(HashFunction func, Path file) { 26 | try { 27 | return HashUtils.hash(func, file).toString(); 28 | } catch (IOException e) { 29 | LOGGER.error("Failed to hash file. {}", file, e); 30 | return "error - " + e.getMessage(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/OS.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os; 2 | 3 | import net.creeperhost.creeperlauncher.os.platform.LinuxPlatform; 4 | import net.creeperhost.creeperlauncher.os.platform.MacosPlatform; 5 | import net.creeperhost.creeperlauncher.os.platform.UnknownPlatform; 6 | import net.creeperhost.creeperlauncher.os.platform.WindowsPlatform; 7 | 8 | import java.util.function.Supplier; 9 | 10 | public enum OS { 11 | WIN(WindowsPlatform::new), 12 | MAC(MacosPlatform::new), 13 | LINUX(LinuxPlatform::new), 14 | UNKNOWN(UnknownPlatform::new); 15 | 16 | public static final OS CURRENT; 17 | 18 | static { 19 | String osName = System.getProperty("os.name").toLowerCase(); 20 | if (osName.contains("win")) { 21 | CURRENT = OS.WIN; 22 | } else if (osName.contains("mac")) { 23 | CURRENT = OS.MAC; 24 | } else if (osName.contains("linux")) { 25 | CURRENT = OS.LINUX; 26 | } else { 27 | CURRENT = OS.UNKNOWN; 28 | } 29 | } 30 | 31 | private final Supplier platformSupplier; 32 | private Platform platformImpl; 33 | 34 | OS(Supplier platformSupplier) { 35 | this.platformSupplier = platformSupplier; 36 | } 37 | 38 | public Platform getPlatform() { 39 | if (this == UNKNOWN) { 40 | throw new PlatformNotSupportedException(); 41 | } 42 | if (platformImpl == null) { 43 | platformImpl = platformSupplier.get(); 44 | } 45 | return platformImpl; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/modloader/ModLoader.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft.modloader; 2 | 3 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 4 | import net.creeperhost.creeperlauncher.util.LoaderTarget; 5 | 6 | import java.io.File; 7 | import java.nio.file.Path; 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | public abstract class ModLoader { 12 | 13 | private final List loaderTargets; 14 | 15 | public ModLoader(List loaderTargets) 16 | { 17 | this.loaderTargets = loaderTargets; 18 | } 19 | 20 | public abstract String getName(); 21 | 22 | public abstract Path install(LocalInstance instance); 23 | 24 | public boolean isApplicable() 25 | { 26 | var optionalTarget = getTargetFromName(getName()); 27 | if (optionalTarget.isPresent()) { 28 | var target = optionalTarget.get(); 29 | return target.getType().equals("modloader") && target.getName().equals(getName()); 30 | } 31 | return getTargetFromName(getName()).isPresent(); 32 | } 33 | 34 | public String getMinecraftVersion() 35 | { 36 | return getTargetVersion("minecraft").orElseThrow(); 37 | } 38 | 39 | public Optional getTargetVersion(String name) 40 | { 41 | return getTargetFromName(name) 42 | .map(LoaderTarget::getVersion); 43 | } 44 | 45 | public Optional getTargetFromName(String name) 46 | { 47 | for (LoaderTarget target : loaderTargets) { 48 | if (target.getName().equals(name)) { 49 | return Optional.of(target); 50 | } 51 | } 52 | return Optional.empty(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/SettingsConfigureHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.api.data.other.SettingsConfigureData; 5 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 6 | import net.creeperhost.creeperlauncher.util.SettingsChangeUtil; 7 | 8 | import java.util.Map; 9 | 10 | public class SettingsConfigureHandler implements IMessageHandler 11 | { 12 | @Override 13 | public void handle(SettingsConfigureData data) 14 | { 15 | boolean anyChanged = false; 16 | for (Map.Entry setting : data.settingsInfo.entrySet()) 17 | { 18 | try { 19 | boolean changed = false; 20 | if (!Settings.settings.containsKey(setting.getKey()) || !Settings.settings.get(setting.getKey()).equals(setting.getValue())) 21 | changed = true; 22 | if (changed) { 23 | if (SettingsChangeUtil.settingsChanged(setting.getKey(), setting.getValue())) { 24 | Settings.settings.remove(setting.getKey()); 25 | Settings.settings.put(setting.getKey(), setting.getValue()); 26 | anyChanged = true; 27 | } 28 | } 29 | } catch (Exception e) { 30 | } 31 | } 32 | if (anyChanged) 33 | { 34 | Settings.saveSettings(); 35 | } 36 | Settings.webSocketAPI.sendMessage(new SettingsConfigureData.Reply(data, "success")); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/window/win/WindowsWindowHelper.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform.window.win; 2 | 3 | import com.sun.jna.Native; 4 | import com.sun.jna.platform.win32.GDI32; 5 | import com.sun.jna.platform.win32.User32; 6 | import com.sun.jna.platform.win32.WinDef; 7 | import com.sun.jna.win32.W32APIOptions; 8 | import net.creeperhost.creeperlauncher.os.platform.window.IWindow; 9 | import net.creeperhost.creeperlauncher.os.platform.window.IWindowHelper; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class WindowsWindowHelper implements IWindowHelper { 15 | @Override 16 | public List getWindows() { 17 | return getWindows(-1); 18 | } 19 | 20 | @Override 21 | public List getWindows(int pid) { 22 | ArrayList tempList = new ArrayList<>(); 23 | User32.INSTANCE.EnumWindows((hWnd, arg) -> { 24 | WindowsWindow windowsWindow = new WindowsWindow(hWnd); 25 | if (pid != -1 && windowsWindow.getPid() != pid) return true; 26 | tempList.add(windowsWindow); 27 | return true; 28 | }, null); 29 | return tempList; 30 | } 31 | 32 | @Override 33 | public void setCursorPos(int x, int y) { 34 | com.sun.jna.platform.win32.User32.INSTANCE.SetCursorPos(x, y); 35 | } 36 | 37 | @Override 38 | public boolean isSupported() { 39 | return true; 40 | } 41 | 42 | public interface OurGDI32 extends GDI32 { 43 | OurGDI32 INSTANCE = Native.load("gdi32", OurGDI32.class, W32APIOptions.DEFAULT_OPTIONS); 44 | int GetPixel(WinDef.HDC hdc, int x, int y); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/BasePlatform.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.os.Platform; 5 | import net.creeperhost.creeperlauncher.os.platform.window.IWindowHelper; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created by covers1624 on 8/3/21. 13 | */ 14 | abstract class BasePlatform implements Platform { 15 | 16 | private final IWindowHelper windowHelper; 17 | 18 | protected BasePlatform(IWindowHelper windowHelper) { 19 | this.windowHelper = windowHelper; 20 | } 21 | 22 | @Override 23 | public final IWindowHelper getWindowHelper() { 24 | return windowHelper; 25 | } 26 | 27 | protected List prepareLauncherProcessArgs() { 28 | List args = new ArrayList<>(); 29 | args.add(getLauncherExecutable().toAbsolutePath().toString()); 30 | args.add("--workDir"); 31 | args.add(Constants.BIN_LOCATION.toAbsolutePath().toString()); 32 | return args; 33 | } 34 | 35 | protected void prepareLauncherEnvironment(ProcessBuilder builder) { 36 | Map environment = builder.environment(); 37 | environment.remove("_JAVA_OPTIONS"); 38 | environment.remove("JAVA_TOOL_OPTIONS"); 39 | environment.remove("JAVA_OPTIONS"); 40 | } 41 | 42 | @Override 43 | public ProcessBuilder buildLauncherProcess() { 44 | ProcessBuilder builder = new ProcessBuilder(prepareLauncherProcessArgs()); 45 | prepareLauncherEnvironment(builder); 46 | return builder; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/window/IWindow.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform.window; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * Represents a window on a specific monitor. 7 | */ 8 | public interface IWindow { 9 | 10 | /** 11 | * Gets a platform specific handle for interacting with this window. 12 | * 13 | * @return The handle. 14 | */ 15 | Object getHandle(); 16 | 17 | /** 18 | * Gets the title of this window. 19 | * 20 | * @return The title. 21 | */ 22 | String getWindowTitle(); 23 | 24 | /** 25 | * Gets the Process ID of the process who owns this window. 26 | * 27 | * @return Rhe PID. 28 | */ 29 | int getPid(); 30 | 31 | /** 32 | * Gets the size of this window. 33 | * 34 | * @return The size. 35 | */ 36 | Rectangle getRect(); 37 | 38 | /** 39 | * Gets the monitor this Window is located on. 40 | * 41 | * @return The window. 42 | */ 43 | IMonitor getMonitor(); 44 | 45 | /** 46 | * Gets the color of a specific pixel on the window. 47 | * 48 | * @param x The X pos 49 | * @param y The Y pos. 50 | * @return The color. 51 | */ 52 | Color getPixelColour(int x, int y); 53 | 54 | /** 55 | * Triggers this window to be brought to the foreground and focussed. 56 | */ 57 | void bringToFront(); 58 | 59 | /** 60 | * If this Window is currently focused 61 | * 62 | * @return If this window has focus. 63 | */ 64 | boolean hasFocus(); 65 | 66 | /** 67 | * If this Window is currently in the foreground. 68 | * 69 | * @return If the Window is in the foreground. 70 | */ 71 | boolean isForeground(); 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/InstalledInstancesHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.Instances; 6 | import net.creeperhost.creeperlauncher.api.data.instances.InstalledInstancesData; 7 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 8 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class InstalledInstancesHandler implements IMessageHandler 14 | { 15 | 16 | @Override 17 | public void handle(InstalledInstancesData data) 18 | { 19 | int id = data.requestId; 20 | boolean refresh = data.refresh; 21 | CompletableFuture.runAsync(() -> { 22 | if(refresh) Instances.refreshInstances(); 23 | List installedInstances; 24 | List cloudInstances; 25 | try { 26 | //TODO, an exception will never be produced here. 27 | Instances.allInstances(); 28 | Instances.cloudInstances(); 29 | } catch(Throwable t) 30 | { 31 | Instances.refreshInstances(); 32 | } finally 33 | { 34 | installedInstances = Instances.allInstances(); 35 | cloudInstances = Instances.cloudInstances(); 36 | } 37 | InstalledInstancesData.Reply reply = new InstalledInstancesData.Reply(id, installedInstances, cloudInstances); 38 | Settings.webSocketAPI.sendMessage(reply); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/window/IWindowHelper.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform.window; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | /** 7 | * Represents a Window Helper for platform specific window interaction. 8 | */ 9 | public interface IWindowHelper { 10 | 11 | /** 12 | * Gets All windows available. 13 | * 14 | * @return The Windows. 15 | */ 16 | default List getWindows() { 17 | return getWindows(-1); 18 | } 19 | 20 | /** 21 | * Gets all the windows owned by a specific Process ID. 22 | * 23 | * @param pid The Process ID to get windows for. 24 | * @return The Windows. 25 | */ 26 | List getWindows(int pid); 27 | 28 | /** 29 | * Sets the current Screen position for the mouse cursor. 30 | * 31 | * @param x The X position. 32 | * @param y The Y position. 33 | */ 34 | void setCursorPos(int x, int y); 35 | 36 | /** 37 | * If this Window Helper is functional or a stub. 38 | * 39 | * @return If the Window Helper is functional. 40 | */ 41 | boolean isSupported(); 42 | 43 | /** 44 | * Basic Null/NoOp implementation of {@link IWindowHelper} 45 | */ 46 | class NullWindowHelper implements IWindowHelper { 47 | 48 | public static final IWindowHelper INSTANCE = new NullWindowHelper(); 49 | 50 | private NullWindowHelper() { 51 | } 52 | 53 | //@formatter:off 54 | @Override public List getWindows(int pid) { return Collections.emptyList(); } 55 | @Override public void setCursorPos(int x, int y) { } 56 | @Override public boolean isSupported() { return false; } 57 | //@formatter:on 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/other/StoreAuthDetailsHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.other; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.CreeperLauncher; 5 | import net.creeperhost.creeperlauncher.Settings; 6 | import net.creeperhost.creeperlauncher.api.data.other.StoreAuthDetailsData; 7 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 8 | import net.creeperhost.creeperlauncher.os.OS; 9 | import net.creeperhost.minetogether.lib.MineTogether; 10 | import net.creeperhost.minetogether.lib.cloudsaves.CloudSaveManager; 11 | import net.creeperhost.minetogether.lib.vpn.MineTogetherConnect; 12 | 13 | public class StoreAuthDetailsHandler implements IMessageHandler 14 | { 15 | @Override 16 | public void handle(StoreAuthDetailsData data) 17 | { 18 | Constants.SECRET = data.mpSecret; 19 | Constants.KEY = data.mpKey; 20 | Constants.MT_HASH = data.mtHash; 21 | Constants.S3_BUCKET = data.s3Bucket; 22 | Constants.S3_HOST = data.s3Host; 23 | Constants.S3_KEY = data.s3Key; 24 | Constants.S3_SECRET = data.s3Secret; 25 | MineTogether.init("modpacklauncher/" + Constants.APPVERSION + " Mozilla/5.0 (" + OS.CURRENT.name() + ") AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.138 Safari/537.36 Vivaldi/1.8.770.56", Constants.KEY, Constants.SECRET, Settings.settings.getOrDefault("sessionString", "")); 26 | CreeperLauncher.mtConnect = new MineTogetherConnect(Constants.MT_HASH, () -> Settings.settings.getOrDefault("mtConnect", "false").equalsIgnoreCase("true"), Constants.MTCONNECT_DIR, Settings.settings.get("sessionString")); 27 | CloudSaveManager.setup(Constants.S3_HOST, 8080, Constants.S3_KEY, Constants.S3_SECRET, Constants.S3_BUCKET); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/InstanceInfoHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.api.data.instances.InstanceInfoData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import net.creeperhost.creeperlauncher.pack.ModPack; 8 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 9 | 10 | import java.util.HashMap; 11 | 12 | public class InstanceInfoHandler implements IMessageHandler 13 | { 14 | @Override 15 | public void handle(InstanceInfoData data) 16 | { 17 | HashMap instanceInfo = new HashMap<>(); 18 | ModPack packInfo = null; 19 | try 20 | { 21 | LocalInstance instance = new LocalInstance(Settings.getInstanceLocOr(Constants.INSTANCES_FOLDER_LOC).resolve(data.uuid)); 22 | instanceInfo.put("uuid", instance.getUuid().toString()); 23 | instanceInfo.put("name", instance.getName()); 24 | instanceInfo.put("memory", String.valueOf(instance.memory)); 25 | instanceInfo.put("jvmargs", instance.jvmArgs); 26 | instanceInfo.put("width", String.valueOf(instance.width)); 27 | instanceInfo.put("height", String.valueOf(instance.height)); 28 | instanceInfo.put("embeddedjre", String.valueOf(instance.embeddedJre)); 29 | packInfo = instance.getManifest(() -> { 30 | Settings.webSocketAPI.sendMessage(new InstanceInfoData.Reply(data, instanceInfo, instance.manifest)); 31 | }); 32 | 33 | } catch (Exception ignored) 34 | { 35 | } 36 | Settings.webSocketAPI.sendMessage(new InstanceInfoData.Reply(data, instanceInfo, packInfo)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstanceInfoData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | import net.creeperhost.creeperlauncher.pack.ModPack; 5 | 6 | import java.awt.*; 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | 10 | public class InstanceInfoData extends BaseData 11 | { 12 | public String uuid; 13 | public Dimension autoResolution = new Dimension(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth() / 2, GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight() / 2); 14 | public HashSet supportedResolutions = new HashSet<>(); 15 | 16 | 17 | public static class Reply extends InstanceInfoData 18 | { 19 | public HashMap instanceInfo; 20 | ModPack packInfo; 21 | 22 | public Reply(InstanceInfoData data, HashMap instanceInfo, ModPack packInfo) 23 | { 24 | type = "instanceInfoReply"; 25 | GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment() 26 | .getScreenDevices(); 27 | for (int i = 0; i < devices.length; i++) 28 | { 29 | GraphicsDevice dev = devices[i]; 30 | DisplayMode[] modes = dev.getDisplayModes(); 31 | for (int j = 0; j < modes.length; j++) 32 | { 33 | DisplayMode m = modes[j]; 34 | supportedResolutions.add(new Dimension(m.getWidth(), m.getHeight())); 35 | } 36 | } 37 | uuid = data.uuid; 38 | requestId = data.requestId; 39 | this.instanceInfo = instanceInfo; 40 | this.packInfo = packInfo; 41 | // TODO: configurable options in InstanceInfoDataReply 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/ShareInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.Instances; 5 | import net.creeperhost.creeperlauncher.Settings; 6 | import net.creeperhost.creeperlauncher.api.data.instances.BrowseInstanceData; 7 | import net.creeperhost.creeperlauncher.api.data.instances.ShareInstanceData; 8 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 9 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 10 | import net.creeperhost.creeperlauncher.share.InstanceData; 11 | import net.creeperhost.creeperlauncher.util.GsonUtils; 12 | import net.creeperhost.creeperlauncher.util.WebUtils; 13 | 14 | import java.io.IOException; 15 | import java.util.UUID; 16 | import java.util.concurrent.CompletableFuture; 17 | 18 | public class ShareInstanceHandler implements IMessageHandler 19 | { 20 | @Override 21 | public void handle(ShareInstanceData data) 22 | { 23 | try 24 | { 25 | CompletableFuture.runAsync(() -> 26 | { 27 | LocalInstance instance = Instances.getInstance(UUID.fromString(data.uuid)); 28 | try 29 | { 30 | InstanceData instanceData = new InstanceData(instance); 31 | String code = instanceData.share(); 32 | 33 | String URL = "https://api.modpacks.ch/" + Constants.KEY + "/modpack/share/" + code; 34 | String json = GsonUtils.GSON.toJson(instanceData); 35 | WebUtils.putWebResponse(URL, json, true, false); 36 | 37 | Settings.webSocketAPI.sendMessage(new ShareInstanceData.Reply(data, "success", "", data.uuid, code)); 38 | } catch (IOException e) { e.printStackTrace(); } 39 | }); 40 | } catch (Exception err) 41 | { 42 | Settings.webSocketAPI.sendMessage(new ShareInstanceData.Reply(data, "error", "", data.uuid, "")); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/SettingsInfoData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | import oshi.SystemInfo; 5 | import oshi.hardware.HardwareAbstractionLayer; 6 | 7 | import java.awt.*; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | 11 | public class SettingsInfoData extends BaseData 12 | { 13 | private static SystemInfo si = new SystemInfo(); 14 | private static HardwareAbstractionLayer hal = si.getHardware(); 15 | 16 | public static class Reply extends SettingsInfoData 17 | { 18 | public HashMap settingsInfo; 19 | public long totalMemory = hal.getMemory().getTotal() / 1024 / 1024; 20 | public long availableMemory = hal.getMemory().getAvailable() / 1024 / 1024; 21 | public int totalCores = hal.getProcessor().getLogicalProcessorCount(); 22 | public Dimension autoResolution = new Dimension(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth() / 2, GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight() / 2); 23 | public HashSet supportedResolutions = new HashSet<>(); 24 | 25 | public Reply(SettingsInfoData data, HashMap settingsInfo) 26 | { 27 | type = "settingsInfoReply"; 28 | GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment() 29 | .getScreenDevices(); 30 | for (int i = 0; i < devices.length; i++) 31 | { 32 | GraphicsDevice dev = devices[i]; 33 | DisplayMode[] modes = dev.getDisplayModes(); 34 | for (int j = 0; j < modes.length; j++) 35 | { 36 | DisplayMode m = modes[j]; 37 | supportedResolutions.add(new Dimension(m.getWidth(), m.getHeight())); 38 | } 39 | } 40 | requestId = data.requestId; 41 | this.settingsInfo = settingsInfo; 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/migration/migrators/DialogUtil.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.migration.migrators; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.api.data.other.CloseModalData; 5 | import net.creeperhost.creeperlauncher.api.data.other.OpenModalData; 6 | 7 | import java.util.List; 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | public class DialogUtil { 11 | public static boolean confirmDialog(String title, String body) { 12 | AtomicBoolean result = new AtomicBoolean(); 13 | 14 | OpenModalData.openModal(title, body, List.of( 15 | new OpenModalData.ModalButton( "Yes", "green", () -> { 16 | result.set(true); 17 | synchronized (result) { 18 | Settings.webSocketAPI.sendMessage(new CloseModalData()); 19 | result.notify(); 20 | } 21 | }), 22 | new OpenModalData.ModalButton("No", "red", () -> { 23 | result.set(false); 24 | synchronized (result) { 25 | Settings.webSocketAPI.sendMessage(new CloseModalData()); 26 | result.notify(); 27 | } 28 | }) 29 | ), false); 30 | 31 | try { 32 | synchronized (result) { 33 | result.wait(); 34 | } 35 | } catch (InterruptedException ignored) { 36 | } 37 | 38 | return result.get(); 39 | } 40 | 41 | public static void okDialog(String title, String body) { 42 | Object lock = new Object(); 43 | 44 | OpenModalData.openModal(title, body, List.of( 45 | new OpenModalData.ModalButton( "Ok", "green", () -> { 46 | synchronized (lock) { 47 | Settings.webSocketAPI.sendMessage(new CloseModalData()); 48 | lock.notify(); 49 | } 50 | }) 51 | ), false); 52 | 53 | try { 54 | synchronized (lock) { 55 | lock.wait(); 56 | } 57 | } catch (InterruptedException ignored) { 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/other/OpenModalData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.other; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.api.data.BaseData; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public class OpenModalData extends BaseData 10 | { 11 | private final String title; 12 | private final String message; 13 | private final boolean dismissable; 14 | public final List buttons; 15 | public static OpenModalData currentlyOpenModal = null; 16 | public String id; 17 | 18 | public OpenModalData(String title, String message, List buttons, boolean dismissable) { 19 | this.type = "openModal"; 20 | this.title = title; 21 | this.message = message; 22 | this.buttons = buttons; 23 | this.id = UUID.randomUUID().toString(); 24 | this.dismissable = dismissable; 25 | } 26 | 27 | public static void openModal(String title, String message, List modalButtons) { 28 | openModal(title, message, modalButtons,true); 29 | } 30 | 31 | public static void openModal(String title, String message, List modalButtons, boolean dismissable) { 32 | currentlyOpenModal = new OpenModalData(title, message, modalButtons, dismissable); 33 | Settings.webSocketAPI.sendMessage(currentlyOpenModal); 34 | } 35 | 36 | public static class ModalButton 37 | { 38 | public final String message; 39 | private final String name; 40 | private final String colour; 41 | public final transient Runnable callback; 42 | 43 | public ModalButton(String name, String colour, Runnable callback) { 44 | this(UUID.randomUUID().toString(), name, colour, callback); 45 | } 46 | 47 | public ModalButton(String message, String name, String colour, Runnable callback) { 48 | this.message = message; 49 | this.name = name; 50 | this.colour = colour; 51 | this.callback = callback; 52 | } 53 | } 54 | 55 | public static class ModalCallbackData extends BaseData 56 | { 57 | public String id; 58 | public String message; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/InstanceModsHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Instances; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.api.data.instances.InstanceModsData; 6 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 7 | import net.creeperhost.creeperlauncher.api.handlers.ModFile; 8 | import net.creeperhost.creeperlauncher.install.tasks.FTBModPackInstallerTask; 9 | import net.creeperhost.creeperlauncher.pack.ModPack; 10 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.UUID; 15 | import java.util.stream.Collectors; 16 | 17 | public class InstanceModsHandler implements IMessageHandler { 18 | @Override 19 | public void handle(InstanceModsData data) { 20 | LocalInstance instance = Instances.getInstance(UUID.fromString(data.uuid)); 21 | ModPack pack = FTBModPackInstallerTask.getPackFromAPI(instance.getId(), instance.getVersionId(), data._private, instance.packType); 22 | if (pack != null) { 23 | List instanceMods = instance.getMods(); 24 | List packMods = pack.getMods(); 25 | List finalMergedMods = new ArrayList<>(); 26 | packMods.forEach(mod -> { 27 | int indexOf = instanceMods.indexOf(mod); 28 | if (indexOf != -1) { 29 | ModFile instanceMod = instanceMods.get(indexOf); 30 | finalMergedMods.add(new ModFile(mod.getName(), mod.getVersion(), instanceMod.getSize(), mod.getSha1()).setExists(true).setExpected(true)); 31 | } else { 32 | finalMergedMods.add(mod); 33 | } 34 | }); 35 | 36 | instanceMods.forEach(mod -> { 37 | if (!finalMergedMods.contains(mod)) { 38 | finalMergedMods.add(mod); 39 | } 40 | }); 41 | List collect = finalMergedMods.stream().sorted((e1, e2) -> e1.getName().compareToIgnoreCase(e2.getName())).collect(Collectors.toList()); 42 | Settings.webSocketAPI.sendMessage(new InstanceModsData.Reply(data, collect)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/LinuxPlatform.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.CreeperLauncher; 5 | import net.creeperhost.creeperlauncher.os.platform.window.IWindowHelper; 6 | import net.creeperhost.creeperlauncher.util.FileUtils; 7 | import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.CompletableFuture; 15 | 16 | /** 17 | * Represents the Linux platform. 18 | *

19 | * Created by covers1624 on 9/2/21. 20 | */ 21 | public class LinuxPlatform extends UnixPlatform { 22 | 23 | private static final String LAUNCHER_URL = "https://launcher.mojang.com/download/Minecraft.tar.gz"; 24 | private static final String LAUNCHER_EXECUTABLE = "minecraft-launcher/minecraft-launcher"; 25 | 26 | public LinuxPlatform() { 27 | super(IWindowHelper.NullWindowHelper.INSTANCE); 28 | } 29 | 30 | @Override 31 | public String getLauncherURL() { 32 | return LAUNCHER_URL; 33 | } 34 | 35 | @Override 36 | public Path getLauncherExecutable() { 37 | return Constants.BIN_LOCATION.resolve(LAUNCHER_EXECUTABLE); 38 | } 39 | 40 | @Override 41 | public void unpackLauncher(Path downloadedLauncher) throws IOException { 42 | FileUtils.unTar(new GzipCompressorInputStream(Files.newInputStream(downloadedLauncher)), Constants.BIN_LOCATION); 43 | chmod755(getLauncherExecutable()); 44 | 45 | //Due to a bug with the vanilla launcher it needs to be started without --workdir on the first start 46 | CompletableFuture.runAsync(() -> 47 | { 48 | try 49 | { 50 | List args = new ArrayList<>(); 51 | args.add(getLauncherExecutable().toAbsolutePath().toString()); 52 | 53 | ProcessBuilder builder = new ProcessBuilder(args); 54 | Process process = builder.start(); 55 | Thread.sleep(1000); 56 | process.destroyForcibly(); 57 | } catch (Exception e) 58 | { 59 | e.printStackTrace(); 60 | } 61 | }).join(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/migration/Migrator.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.migration; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * A Migrator, capable of migrating between 2 distinct Data Format versions. 10 | *

11 | * A Migrator should be able to properly handle execution failing, or stopping half way through 12 | * and resuming its operations. Any internal state required by other Migrators should be re-setup 13 | * when execution resumes. However, it is acceptable to yeet the data and start from scratch. 14 | *

15 | * You are required to have a {@link Properties} annotation on your Migrator implementation, 16 | * in order to specify what Data format you read from and write to. You can also declare any 17 | * other Migrators you depend on. 18 | *

19 | * Created by covers1624 on 13/1/21. 20 | */ 21 | public interface Migrator { 22 | 23 | /** 24 | * Operates on the current context. 25 | * Your {@link Properties#requires()} have already been run 26 | * as well as any prior migrators. 27 | * 28 | * @param ctx The context to operate on. 29 | */ 30 | void operate(MigrationContext ctx) throws MigrationException; 31 | 32 | /** 33 | * Defines properties for a Migrator. 34 | */ 35 | @Target (ElementType.TYPE) 36 | @Retention (RetentionPolicy.RUNTIME) 37 | @interface Properties { 38 | 39 | /** 40 | * @return The Data Format version this Handler is capable of migrating from. 41 | */ 42 | int from(); 43 | 44 | /** 45 | * @return The Data Format version this Handler Migrates to. 46 | */ 47 | int to(); 48 | 49 | /** 50 | * Specifies which Migrators this one must run after. 51 | * 52 | * @return The other migrators. 53 | */ 54 | Class[] requires() default {}; 55 | } 56 | 57 | /** 58 | * Thrown when a fatal error occurred whilst executing a migrator. 59 | */ 60 | class MigrationException extends Exception { 61 | 62 | public MigrationException(String message) { 63 | super(message); 64 | } 65 | 66 | public MigrationException(String message, Throwable cause) { 67 | super(message, cause); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstallInstanceData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.api.data.BaseData; 4 | import net.creeperhost.creeperlauncher.install.tasks.FTBModPackInstallerTask; 5 | 6 | public class InstallInstanceData extends BaseData 7 | { 8 | public static String typePrefix = "install"; 9 | public String uuid; 10 | public long id; 11 | public long version; 12 | public boolean _private = false; 13 | public byte packType = 0; 14 | 15 | public static class Reply extends BaseData 16 | { 17 | final String status; 18 | final String message; 19 | final String uuid; 20 | 21 | public Reply(InstallInstanceData data, String status, String message, String uuid) 22 | { 23 | type = typePrefix + "InstanceDataReply"; 24 | requestId = data.requestId; 25 | this.status = status; 26 | this.message = message; 27 | this.uuid = uuid; 28 | // Todo: get tasks from install, update corresponding TaskData objects and send update to other side 29 | } 30 | } 31 | 32 | public static class Progress extends BaseData 33 | { 34 | //final HashMap tasks; 35 | final Double overallPercentage; 36 | final long speed; 37 | final long currentBytes; 38 | final long overallBytes; 39 | final FTBModPackInstallerTask.Stage currentStage; 40 | 41 | public Progress(InstallInstanceData data, Double overallPercentage, long speed, long currentBytes, long overallBytes, FTBModPackInstallerTask.Stage currentStage) 42 | { 43 | this.requestId = data.requestId; 44 | type = typePrefix + "InstanceProgress"; 45 | //this.tasks = tasks; 46 | // TODO: pass in tasks that have updated 47 | this.overallPercentage = overallPercentage; 48 | this.speed = speed; 49 | this.currentBytes = currentBytes; 50 | this.overallBytes = overallBytes; 51 | this.currentStage = currentStage; 52 | } 53 | } 54 | 55 | public static class TaskData 56 | { 57 | int id; 58 | String taskName; 59 | int progress; 60 | 61 | public TaskData(int id, String taskName, int progress) 62 | { 63 | this.id = id; 64 | this.taskName = taskName; 65 | this.progress = progress; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/MacosPlatform.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform; 2 | 3 | import net.covers1624.quack.io.CopyingFileVisitor; 4 | import net.covers1624.quack.io.IOUtils; 5 | import net.creeperhost.creeperlauncher.Constants; 6 | import net.creeperhost.creeperlauncher.os.platform.window.IWindowHelper; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.FileSystem; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.util.List; 13 | 14 | /** 15 | * Represents the MacOS platform. 16 | *

17 | * Created by covers1624 on 9/2/21. 18 | */ 19 | public class MacosPlatform extends UnixPlatform { 20 | 21 | private static final String LAUNCHER_URL = "https://apps.modpacks.ch/FTB2/mac.zip"; 22 | private static final String LAUNCHER_EXECUTABLE = "Minecraft.app/Contents/MacOS/launcher"; 23 | private static final String[] ADDITIONAL_EXECUTABLES = { 24 | LAUNCHER_EXECUTABLE, 25 | "Minecraft.app/Contents/Minecraft Updater.app/Contents/MacOS/nativeUpdater" 26 | }; 27 | 28 | public MacosPlatform() { 29 | super(IWindowHelper.NullWindowHelper.INSTANCE); 30 | } 31 | 32 | @Override 33 | public String getLauncherURL() { 34 | return LAUNCHER_URL; 35 | } 36 | 37 | @Override 38 | public Path getLauncherExecutable() { 39 | return Constants.BIN_LOCATION.resolve(LAUNCHER_EXECUTABLE); 40 | } 41 | 42 | public Path getLauncherOpenPath() { 43 | return Constants.BIN_LOCATION.resolve("Minecraft.app"); 44 | } 45 | 46 | @Override 47 | public void unpackLauncher(Path downloadedLauncher) throws IOException { 48 | try (FileSystem fs = IOUtils.getJarFileSystem(downloadedLauncher, true)) { 49 | Path root = fs.getPath("/"); 50 | Files.walkFileTree(root, new CopyingFileVisitor(root, Constants.BIN_LOCATION)); 51 | } 52 | for (String executable : ADDITIONAL_EXECUTABLES) { 53 | //TODO, UnixPlatform.chmod755? Should be supported by MacOS? 54 | Constants.BIN_LOCATION.resolve(executable).toFile().setExecutable(true); 55 | } 56 | } 57 | 58 | @Override //Macos is speshal, this is validated with unit test. 59 | protected List prepareLauncherProcessArgs() { 60 | List args = super.prepareLauncherProcessArgs(); 61 | args.add(0, "/usr/bin/open"); 62 | args.set(1, getLauncherOpenPath().toAbsolutePath().toString()); 63 | args.add(2, "--args"); 64 | return args; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /graal.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "net.creeperhost.creeperlauncher.api.data.browseInstanceData", 4 | "allPublicFields": true, 5 | "methods": [ 6 | { "name": "", "parameterTypes": [] } 7 | ] 8 | }, 9 | { 10 | "name": "net.creeperhost.creeperlauncher.api.data.CancelInstallInstanceData", 11 | "allPublicFields": true, 12 | "methods": [ 13 | { "name": "", "parameterTypes": [] } 14 | ] 15 | }, 16 | { 17 | "name": "net.creeperhost.creeperlauncher.api.data.InstalledInstancesData", 18 | "allPublicFields": true, 19 | "methods": [ 20 | { "name": "", "parameterTypes": [] } 21 | ] 22 | }, 23 | { 24 | "name": "net.creeperhost.creeperlauncher.api.data.InstallInstanceData", 25 | "allPublicFields": true, 26 | "methods": [ 27 | { "name": "", "parameterTypes": [] } 28 | ] 29 | }, 30 | { 31 | "name": "net.creeperhost.creeperlauncher.api.data.InstanceConfigureData", 32 | "allPublicFields": true, 33 | "methods": [ 34 | { "name": "", "parameterTypes": [] } 35 | ] 36 | }, 37 | { 38 | "name": "net.creeperhost.creeperlauncher.api.data.InstanceInfoData", 39 | "allPublicFields": true, 40 | "methods": [ 41 | { "name": "", "parameterTypes": [] } 42 | ] 43 | }, 44 | { 45 | "name": "net.creeperhost.creeperlauncher.api.data.LaunchInstanceData", 46 | "allPublicFields": true, 47 | "methods": [ 48 | { "name": "", "parameterTypes": [] } 49 | ] 50 | }, 51 | { 52 | "name": "net.creeperhost.creeperlauncher.api.data.settingsConfigureData", 53 | "allPublicFields": true, 54 | "methods": [ 55 | { "name": "", "parameterTypes": [] } 56 | ] 57 | }, 58 | { 59 | "name": "net.creeperhost.creeperlauncher.api.data.settingsInfoData", 60 | "allPublicFields": true, 61 | "methods": [ 62 | { "name": "", "parameterTypes": [] } 63 | ] 64 | }, 65 | { 66 | "name": "net.creeperhost.creeperlauncher.api.data.UninstallInstanceData", 67 | "allPublicFields": true, 68 | "methods": [ 69 | { "name": "", "parameterTypes": [] } 70 | ] 71 | }, 72 | { 73 | "name": "net.creeperhost.creeperlauncher.api.data.UpdateInstanceData", 74 | "allPublicFields": true, 75 | "methods": [ 76 | { "name": "", "parameterTypes": [] } 77 | ] 78 | }, 79 | { 80 | "name": "net.creeperhost.creeperlauncher.api.data.WindowControlData", 81 | "allPublicFields": true, 82 | "methods": [ 83 | { "name": "", "parameterTypes": [] } 84 | ] 85 | } 86 | ] -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/irc/IRCConnectHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.irc; 2 | 3 | import net.creeperhost.creeperlauncher.Settings; 4 | import net.creeperhost.creeperlauncher.api.data.friends.OnlineFriendData; 5 | import net.creeperhost.creeperlauncher.api.data.irc.IRCConnectData; 6 | import net.creeperhost.creeperlauncher.api.data.irc.IRCPartyInviteData; 7 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 8 | import net.creeperhost.minetogether.lib.chat.ChatHandler; 9 | 10 | import net.creeperhost.creeperlauncher.api.data.friends.AcceptedFriendData; 11 | import net.creeperhost.creeperlauncher.api.data.irc.IRCEventMessageData; 12 | import net.creeperhost.minetogether.lib.chat.IChatListener; 13 | import net.creeperhost.minetogether.lib.chat.data.Message; 14 | import net.creeperhost.minetogether.lib.chat.data.Profile; 15 | 16 | public class IRCConnectHandler implements IMessageHandler 17 | { 18 | @Override 19 | public void handle(IRCConnectData data) { 20 | if(!ChatHandler.isOnline()) { 21 | ChatHandler.init(data.nick, data.realname, new IChatListener() { 22 | @Override 23 | public void onPartyInvite(Profile profile) { 24 | Settings.webSocketAPI.sendMessage(new IRCPartyInviteData(profile)); 25 | } 26 | 27 | @Override 28 | public void onFriendOnline(Profile profile) { 29 | Settings.webSocketAPI.sendMessage(new OnlineFriendData(profile)); 30 | } 31 | 32 | @Override 33 | public void onFriendAccept(String name) { 34 | Settings.webSocketAPI.sendMessage(new AcceptedFriendData(name)); 35 | } 36 | 37 | // We're being asked what server we're on - we shouldn't reply 38 | @Override 39 | public String onServerIdRequest() { 40 | return null; 41 | } 42 | 43 | // message received 44 | @Override 45 | public void sendMessage(Message message) { 46 | Settings.webSocketAPI.sendMessage(new IRCEventMessageData(message.messageStr, message.sender)); 47 | } 48 | 49 | @Override 50 | public void setHasNewMessage(boolean value) { 51 | // nothing done with this yet, mainly used for banned users in the main chat which won't be in the app 52 | } 53 | }, true); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/data/instances/InstanceInstallModData.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.data.instances; 2 | 3 | import net.creeperhost.creeperlauncher.CreeperLauncher; 4 | import net.creeperhost.creeperlauncher.Instances; 5 | import net.creeperhost.creeperlauncher.api.data.BaseData; 6 | import net.creeperhost.creeperlauncher.api.handlers.instances.InstanceInstallModHandler; 7 | import net.creeperhost.creeperlauncher.install.tasks.FTBModPackInstallerTask; 8 | import net.creeperhost.creeperlauncher.mod.Mod; 9 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class InstanceInstallModData extends BaseData { 15 | public String uuid; 16 | public int modId; 17 | public int versionId; 18 | 19 | public static class Reply extends BaseData 20 | { 21 | final String status; 22 | final String message; 23 | final List dependencyList; 24 | 25 | public Reply(InstanceInstallModData data, String status, String message, List dependencies) 26 | { 27 | type = "instanceInstallModReply"; 28 | requestId = data.requestId; 29 | this.status = status; 30 | this.message = message; 31 | this.dependencyList = dependencies; 32 | } 33 | } 34 | 35 | public static class Progress extends BaseData { 36 | final Double overallPercentage; 37 | final long speed; 38 | final long currentBytes; 39 | final long overallBytes; 40 | 41 | public Progress(InstanceInstallModData data, Double overallPercentage, long speed, long currentBytes, long overallBytes) { 42 | this.requestId = data.requestId; 43 | type = "instanceInstallModProgress"; 44 | this.overallPercentage = overallPercentage; 45 | this.speed = speed; 46 | this.currentBytes = currentBytes; 47 | this.overallBytes = overallBytes; 48 | } 49 | 50 | } 51 | 52 | public static void main(String[] args) { 53 | CreeperLauncher.initSettingsAndCache(); 54 | Instances.refreshInstances(); 55 | LocalInstance localInstance = Instances.allInstances().get(0); 56 | InstanceInstallModData instanceInstallModData = new InstanceInstallModData(); 57 | instanceInstallModData.modId = 222880; 58 | instanceInstallModData.versionId = 3099576; 59 | instanceInstallModData.uuid = localInstance.getUuid().toString(); 60 | new InstanceInstallModHandler().handle(instanceInstallModData); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/IntegrityCheckException.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher; 2 | 3 | import com.google.common.hash.HashCode; 4 | 5 | import java.nio.file.Path; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class IntegrityCheckException extends RuntimeException 10 | { 11 | private Throwable otherThrowable = null; 12 | private int errorCode = 0; 13 | private HashCode checksum = null; 14 | private List checksums = new ArrayList<>(); 15 | private long size = 0; 16 | private long expectedSize = 0; 17 | private String source = ""; 18 | private Path destination; 19 | 20 | public IntegrityCheckException(String detailMessage) 21 | { 22 | super(detailMessage); 23 | } 24 | 25 | public IntegrityCheckException(Throwable t, int errorCode, HashCode checksum, List checksums, long size, long expectedSize, String source, Path destination) 26 | { 27 | this(t.getMessage(), errorCode, checksum, checksums, size, expectedSize, source, destination); 28 | initCause(t); 29 | otherThrowable = t; 30 | } 31 | 32 | public IntegrityCheckException(String detailMessage, int errorCode, HashCode checksum, List checksums, long size, long expectedSize, String source, Path destination) 33 | { 34 | super(detailMessage); 35 | this.errorCode = errorCode; 36 | this.checksum = checksum; 37 | this.checksums = checksums; 38 | this.size = size; 39 | this.expectedSize = expectedSize; 40 | this.source = source; 41 | this.destination = destination; 42 | } 43 | 44 | @Override 45 | public String getMessage() { 46 | StringBuilder errorString = new StringBuilder(); 47 | if (otherThrowable != null) 48 | { 49 | errorString.append("Caught throwable: ").append(otherThrowable.getMessage()).append("\n"); 50 | } 51 | errorString.append("errorCode: ").append(errorCode).append("\n"); 52 | errorString.append("checksum: ").append(checksum).append("\n"); 53 | for (HashCode validChecksum : checksums) 54 | { 55 | errorString.append("validChecksum: ").append(validChecksum).append("\n"); 56 | } 57 | errorString.append("size: ").append(size).append("\n"); 58 | errorString.append("expectedSize: ").append(expectedSize).append("\n"); 59 | errorString.append("source: ").append(source).append("\n"); 60 | errorString.append("destination: ").append(destination == null ? "" : destination.toString()).append("\n"); 61 | return super.getMessage() + "\n" + errorString.toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/modloader/ModLoaderManager.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft.modloader; 2 | 3 | import net.creeperhost.creeperlauncher.minecraft.modloader.fabric.FabricModLoader; 4 | import net.creeperhost.creeperlauncher.minecraft.modloader.forge.ForgeInstallerModLoader; 5 | import net.creeperhost.creeperlauncher.minecraft.modloader.forge.ForgeJarModLoader; 6 | import net.creeperhost.creeperlauncher.minecraft.modloader.forge.ForgeUniversalModLoader; 7 | import net.creeperhost.creeperlauncher.util.LoaderTarget; 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | import java.lang.reflect.Array; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | public class ModLoaderManager { 17 | 18 | private static final Logger LOGGER = LogManager.getLogger(); 19 | 20 | private static final List> MOD_LOADER_FACTORIES = new ArrayList<>(); 21 | 22 | public static final ModLoaderFactory FORGE_INSTALLER = register(ForgeInstallerModLoader::new); 23 | public static final ModLoaderFactory FORGE_UNIVERSAL = register(ForgeUniversalModLoader::new); 24 | public static final ModLoaderFactory FORGE_JAR = register(ForgeJarModLoader::new); 25 | public static final ModLoaderFactory FABRIC = register(FabricModLoader::new); 26 | 27 | private static ModLoaderFactory register(ModLoaderFactory modLoaderFactory) 28 | { 29 | MOD_LOADER_FACTORIES.add(modLoaderFactory); 30 | return modLoaderFactory; 31 | } 32 | 33 | public static List getModLoaders(List loaderTargets) 34 | { 35 | //TODO This is deduplicate the list, This should not be needed but fixes an outstanding issue 36 | List output = new ArrayList<>(); 37 | for(ModLoaderFactory factory : MOD_LOADER_FACTORIES) 38 | { 39 | ModLoader target = factory.create(loaderTargets); 40 | if(!output.contains(target) && target.isApplicable()) 41 | { 42 | boolean exists = false; 43 | for(ModLoader modLoader : output) 44 | { 45 | if(modLoader.getName().equals(target.getName())) 46 | { 47 | exists = true; 48 | } 49 | } 50 | if(!exists) 51 | { 52 | output.add(target); 53 | } 54 | } 55 | } 56 | output.forEach(modLoader -> LOGGER.debug(modLoader.getName())); 57 | return output; 58 | // return MOD_LOADER_FACTORIES.stream() 59 | // .map(modLoaderFactory -> modLoaderFactory.create(loaderTargets)) 60 | // .filter(ModLoader::isApplicable) 61 | // .collect(Collectors.toList()); 62 | } 63 | 64 | @FunctionalInterface 65 | public interface ModLoaderFactory 66 | { 67 | T create(List loaderTargets); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/ModFile.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | 7 | public class ModFile { 8 | private final transient String realName; 9 | private final String name; 10 | private final String version; 11 | private final long size; 12 | private final String sha1; 13 | private boolean expected; 14 | private boolean exists; 15 | private boolean enabled; 16 | private Path realPath; 17 | private final transient int hashCode; 18 | public ModFile(String name, String version, long size, String sha1) { 19 | this.realName = name; 20 | this.name = name.replace(".disabled", ""); 21 | this.version = version; 22 | this.size = size; 23 | this.sha1 = sha1; 24 | this.enabled = !realName.endsWith(".disabled"); 25 | this.hashCode = this.realName.toLowerCase().hashCode(); 26 | } 27 | 28 | public ModFile setExpected(boolean expected) { 29 | this.expected = expected; 30 | return this; 31 | } 32 | 33 | public ModFile setExists(boolean exists) { 34 | this.exists = exists; 35 | return this; 36 | } 37 | 38 | public ModFile setPath(Path path) { 39 | realPath = path; 40 | return this; 41 | } 42 | 43 | public boolean setEnabled(boolean state) { 44 | if (realPath == null) { 45 | return false; 46 | } 47 | if (enabled == state) { 48 | return true; 49 | } 50 | Path parent = realPath.getParent(); 51 | String oldName = realPath.getFileName().toString(); 52 | String newFileName = state ? oldName.replace(".disabled", "") : oldName + ".disabled"; 53 | Path newPath = parent.resolve(newFileName); 54 | try { 55 | Files.move(realPath, newPath); 56 | } catch (IOException exception) { 57 | return false; 58 | } 59 | 60 | enabled = state; 61 | realPath = newPath; 62 | return true; 63 | } 64 | 65 | public String getName() { 66 | return realName; 67 | } 68 | 69 | @Override 70 | public int hashCode() { 71 | return hashCode; 72 | } 73 | 74 | @Override 75 | public boolean equals(Object o) { 76 | return o != null && o.getClass() == this.getClass() && o.hashCode() == this.hashCode(); 77 | } 78 | 79 | public String getSha1() { 80 | return sha1; 81 | } 82 | 83 | public long getSize() { 84 | return size; 85 | } 86 | 87 | public String getVersion() { 88 | return version; 89 | } 90 | 91 | public static boolean isPotentialMod(String name) { 92 | String replace = name.replace(".disabled", ""); 93 | return replace.endsWith(".jar") || replace.endsWith(".zip"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/InstanceConfigureHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.Settings; 5 | import net.creeperhost.creeperlauncher.Instances; 6 | import net.creeperhost.creeperlauncher.api.data.instances.InstanceConfigureData; 7 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 8 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 9 | 10 | import java.nio.file.Paths; 11 | import java.util.Map; 12 | import java.util.UUID; 13 | 14 | public class InstanceConfigureHandler implements IMessageHandler 15 | { 16 | @Override 17 | public void handle(InstanceConfigureData data) 18 | { 19 | try 20 | { 21 | //TODO, instance lookup? 22 | LocalInstance instance = new LocalInstance(Settings.getInstanceLocOr(Constants.INSTANCES_FOLDER_LOC).resolve(data.uuid)); 23 | for (Map.Entry setting : data.instanceInfo.entrySet()) 24 | { 25 | switch (setting.getKey().toLowerCase()) 26 | { 27 | case "memory": 28 | instance.memory = Integer.parseInt(setting.getValue()); 29 | break; 30 | case "name": 31 | instance.name = setting.getValue(); 32 | break; 33 | case "jvmargs": 34 | instance.jvmArgs = setting.getValue(); 35 | break; 36 | case "width": 37 | instance.width = Integer.parseInt(setting.getValue()); 38 | break; 39 | case "height": 40 | instance.height = Integer.parseInt(setting.getValue()); 41 | break; 42 | case "cloudsaves": 43 | instance.cloudSaves = Boolean.parseBoolean(setting.getValue()); 44 | break; 45 | case "jrepath": 46 | if(setting.getValue().length() == 0){ 47 | instance.embeddedJre = true; 48 | instance.jrePath = null; 49 | } else { 50 | instance.embeddedJre = false; 51 | instance.jrePath = Paths.get(setting.getValue()); 52 | } 53 | break; 54 | } 55 | } 56 | instance.saveJson(); 57 | Instances.refreshInstances(); 58 | Settings.webSocketAPI.sendMessage(new InstanceConfigureData.Reply(data, "success")); 59 | } catch (Exception err) 60 | { 61 | Settings.webSocketAPI.sendMessage(new InstanceConfigureData.Reply(data, "error")); 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/modloader/forge/ForgeInstallerModLoader.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft.modloader.forge; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.minecraft.McUtils; 5 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 6 | import net.creeperhost.creeperlauncher.util.DownloadUtils; 7 | import net.creeperhost.creeperlauncher.util.ForgeUtils; 8 | import net.creeperhost.creeperlauncher.util.LoaderTarget; 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.util.List; 16 | 17 | public class ForgeInstallerModLoader extends ForgeModLoader 18 | { 19 | private static final Logger LOGGER = LogManager.getLogger(); 20 | 21 | public ForgeInstallerModLoader(List loaderTargets) 22 | { 23 | super(loaderTargets); 24 | } 25 | 26 | @Override 27 | public String getName() 28 | { 29 | return "forge"; 30 | } 31 | 32 | @Override 33 | public Path install(LocalInstance instance) 34 | { 35 | instance.modLoader = getMinecraftVersion() + "-forge-" + getForgeVersion(); 36 | 37 | String forgeUrl = Constants.FORGE_CH + getMinecraftVersion() + "-" + getForgeVersion() + "/forge-" + getMinecraftVersion() + "-" + getForgeVersion() + "-installer.jar"; 38 | String forgeUrlJson = Constants.FORGE_CH + getMinecraftVersion() + "-" + getForgeVersion() + "/forge-" + getMinecraftVersion() + "-" + getForgeVersion() + "-installer.json"; 39 | 40 | LOGGER.info("Attempting to download {}.", forgeUrl); 41 | Path installerFile = instance.getDir().resolve("installer.jar"); 42 | Path installerJson = instance.getDir().resolve("installer.json"); 43 | 44 | DownloadUtils.downloadFile(installerFile, forgeUrl); 45 | DownloadUtils.downloadFile(installerJson, forgeUrlJson); 46 | 47 | if(Files.notExists(installerJson)) 48 | { 49 | //If we do not have the file lets extract it from the installer jar 50 | ForgeUtils.extractJson(installerFile, "installer.json"); 51 | } 52 | 53 | 54 | ForgeUtils.runForgeInstaller(installerFile.toAbsolutePath()); 55 | McUtils.removeProfile(Constants.LAUNCHER_PROFILES_JSON, "forge"); 56 | try { 57 | Files.delete(installerFile); 58 | } catch (IOException ignored) {} 59 | 60 | 61 | return installerJson; 62 | } 63 | 64 | @Override 65 | public boolean isApplicable() { 66 | int minorMcVersion = McUtils.parseMinorVersion(getTargetVersion("minecraft").orElse("0.0.0")); 67 | //1.13 onwards 68 | 69 | if (minorMcVersion == 12) { 70 | String[] versions = getForgeVersion().split("\\."); 71 | try { 72 | int forgeVer = Integer.parseInt(versions[versions.length - 1]); 73 | return super.isApplicable() && forgeVer >= 2851; 74 | } catch (NumberFormatException ignored) { 75 | // ¯\_(ツ)_/¯ 76 | } 77 | } 78 | return super.isApplicable() && minorMcVersion >= 13; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/Artifact.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import com.google.gson.*; 4 | 5 | import java.lang.reflect.Type; 6 | import java.nio.file.Path; 7 | 8 | public class Artifact 9 | { 10 | private String domain; 11 | private String name; 12 | private String version; 13 | private String classifier = null; 14 | private String ext = "jar"; 15 | 16 | private String path; 17 | private String file; 18 | private String descriptor; 19 | 20 | public static Artifact from(String descriptor) 21 | { 22 | Artifact ret = new Artifact(); 23 | ret.descriptor = descriptor; 24 | 25 | String[] pts = descriptor.split(":"); 26 | ret.domain = pts[0]; 27 | ret.name = pts[1]; 28 | 29 | int last = pts.length - 1; 30 | int idx = pts[last].indexOf('@'); 31 | if (idx != -1) 32 | { 33 | ret.ext = pts[last].substring(idx + 1); 34 | pts[last] = pts[last].substring(0, idx); 35 | } 36 | 37 | ret.version = pts[2]; 38 | if (pts.length > 3) 39 | ret.classifier = pts[3]; 40 | 41 | ret.file = ret.name + '-' + ret.version; 42 | if (ret.classifier != null) ret.file += '-' + ret.classifier; 43 | ret.file += '.' + ret.ext; 44 | 45 | ret.path = ret.domain.replace('.', '/') + '/' + ret.name + '/' + ret.version + '/' + ret.file; 46 | 47 | return ret; 48 | } 49 | 50 | public Path getLocalPath(Path base) 51 | { 52 | return base.resolve(path); 53 | } 54 | 55 | public String getDescriptor() 56 | { 57 | return descriptor; 58 | } 59 | 60 | public String getPath() 61 | { 62 | return path; 63 | } 64 | 65 | public String getDomain() 66 | { 67 | return domain; 68 | } 69 | 70 | public String getName() 71 | { 72 | return name; 73 | } 74 | 75 | public String getVersion() 76 | { 77 | return version; 78 | } 79 | 80 | public String getClassifier() 81 | { 82 | return classifier; 83 | } 84 | 85 | public String getExt() 86 | { 87 | return ext; 88 | } 89 | 90 | public String getFilename() 91 | { 92 | return file; 93 | } 94 | 95 | @Override 96 | public String toString() 97 | { 98 | return getDescriptor(); 99 | } 100 | 101 | public static class Adapter implements JsonDeserializer, JsonSerializer 102 | { 103 | @Override 104 | public JsonElement serialize(Artifact src, Type typeOfSrc, JsonSerializationContext context) 105 | { 106 | return src == null ? JsonNull.INSTANCE : new JsonPrimitive(src.getDescriptor()); 107 | } 108 | 109 | @Override 110 | public Artifact deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException 111 | { 112 | return json.isJsonPrimitive() ? Artifact.from(json.getAsJsonPrimitive().getAsString()) : null; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/GsonUtils.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import com.google.common.hash.HashCode; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import net.covers1624.quack.gson.HashCodeAdapter; 7 | import net.covers1624.quack.gson.PathTypeAdapter; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.BufferedWriter; 11 | import java.io.IOException; 12 | import java.lang.reflect.Type; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | 16 | @SuppressWarnings ("UnstableApiUsage") 17 | public class GsonUtils { 18 | 19 | public static Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping() 20 | .registerTypeAdapter(Artifact.class, new Artifact.Adapter()) 21 | .registerTypeAdapter(Path.class, new PathTypeAdapter()) 22 | .registerTypeAdapter(HashCode.class, new HashCodeAdapter()) 23 | .create(); 24 | 25 | /** 26 | * Deserializes a Json file from the given Path as the given Type. 27 | *

28 | * Type can either be a static field using a Gson TypeToken for classes with Generic parameters as shown here:
29 | * Type type = new com.google.gson.reflect.TypeToken<Map<String,String>>(){}.getType();
30 | * Or a Class instance such as: JsonObject.class 31 | * 32 | * @param path The Path to read the json from. 33 | * @param type The Type to Deserialize to. 34 | * @return The Deserialized object. 35 | * @throws IOException Thrown if there was an IO error. 36 | */ 37 | public static T loadJson(Path path, Type type) throws IOException { 38 | try (BufferedReader reader = Files.newBufferedReader(path)) { 39 | return GSON.fromJson(reader, type); 40 | } 41 | } 42 | 43 | /** 44 | * This is an overload of {@link #saveJson(Path, Object, Type)}, defaulting 45 | * to the Class of the provided object for the Type. This will only work for 46 | * simple types such as JsonObject, MyFancyJson. If you have a type with Generics 47 | * Such as a Map, you will want to use the other method and provide your type token as explained. 48 | * 49 | * @param path The path to write the json to. 50 | * @param thing The Object we are serializing. 51 | * @throws IOException Thrown if there was an IO error. 52 | */ 53 | public static void saveJson(Path path, Object thing) throws IOException { 54 | saveJson(path, thing, thing.getClass()); 55 | } 56 | 57 | /** 58 | * Serializes an object to Json, storing it in provided Path. 59 | *

60 | * Type can either be a static field using a Gson TypeToken for classes with Generic parameters as shown here:
61 | * Type type = new com.google.gson.reflect.TypeToken<Map<String,String>>(){}.getType();
62 | * Or a Class instance such as: JsonObject.class 63 | * 64 | * @param path The Path to write the json to. 65 | * @param thing The Object we are serializing. 66 | * @param type The Type to Deserialize to. 67 | * @throws IOException Thrown if there was an IO error. 68 | */ 69 | public static void saveJson(Path path, Object thing, Type type) throws IOException { 70 | try (BufferedWriter writer = Files.newBufferedWriter(path)) { 71 | GSON.toJson(thing, type, writer); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/DownloadUtils.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.net.URLConnection; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.StandardCopyOption; 14 | import java.util.stream.Collectors; 15 | 16 | public class DownloadUtils 17 | { 18 | public static boolean downloadFile(Path target, String url) 19 | { 20 | return downloadFile(target, url, false); 21 | } 22 | 23 | public static boolean downloadFile(Path target, String url, boolean auth) 24 | { 25 | try 26 | { 27 | URLConnection connection = getConnection(url, auth); 28 | if (connection != null && connection.getInputStream() != null) 29 | { 30 | Files.copy(connection.getInputStream(), target, StandardCopyOption.REPLACE_EXISTING); 31 | return true; 32 | } 33 | } catch (IOException ignored) 34 | { 35 | } 36 | return false; 37 | } 38 | 39 | private static URLConnection getConnection(String address) 40 | { 41 | return getConnection(address, false); 42 | } 43 | 44 | private static URLConnection getConnection(String address, boolean auth) 45 | { 46 | try 47 | { 48 | int MAX = 3; 49 | URL url = new URL(address); 50 | URLConnection connection = null; 51 | for (int x = 0; x < MAX; x++) 52 | { 53 | connection = url.openConnection(); 54 | if (auth) 55 | { 56 | if(!Constants.KEY.isEmpty() | !Constants.SECRET.isEmpty()) 57 | { 58 | connection.addRequestProperty("USER_SECRET", Constants.SECRET); 59 | } 60 | } 61 | connection.setConnectTimeout(5000); 62 | connection.setReadTimeout(5000); 63 | if (connection instanceof HttpURLConnection) 64 | { 65 | HttpURLConnection hcon = (HttpURLConnection) connection; 66 | hcon.setInstanceFollowRedirects(false); 67 | int res = hcon.getResponseCode(); 68 | if (res == HttpURLConnection.HTTP_MOVED_PERM || res == HttpURLConnection.HTTP_MOVED_TEMP) 69 | { 70 | String location = hcon.getHeaderField("Location"); 71 | hcon.disconnect(); 72 | url = new URL(url, location); 73 | } else 74 | { 75 | break; 76 | } 77 | } else 78 | { 79 | break; 80 | } 81 | } 82 | return connection; 83 | } catch (IOException e) 84 | { 85 | e.printStackTrace(); 86 | return null; 87 | } 88 | } 89 | 90 | public static String urlToString(URL url) throws IOException { 91 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) { 92 | return reader.lines().collect(Collectors.joining("\n")); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/pack/ModPack.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.pack; 2 | 3 | import net.creeperhost.creeperlauncher.api.SimpleDownloadableFile; 4 | import net.creeperhost.creeperlauncher.api.handlers.ModFile; 5 | import org.apache.logging.log4j.Level; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | import java.nio.file.Path; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | public class ModPack implements IPack 15 | { 16 | private final String name; 17 | private final String version; 18 | private final Path dir; 19 | private final List authors; 20 | private final String description; 21 | private final String mcVersion; 22 | private final String url; 23 | private final String artUrl; 24 | private final int minMemory; 25 | private final int recMemory; 26 | private final long id; 27 | private final List files; 28 | 29 | public ModPack(String name, String version, Path dir, List authors, String description, String mcVersion, String URL, String artUrl, long id, int minMemory, int recMemory, List files) 30 | { 31 | this.name = name; 32 | this.version = version; 33 | this.dir = dir; 34 | this.authors = authors; 35 | this.description = description; 36 | this.mcVersion = mcVersion; 37 | this.url = URL; 38 | this.artUrl = artUrl; 39 | this.id = id; 40 | this.minMemory = minMemory; 41 | this.recMemory = recMemory; 42 | this.files = files; 43 | } 44 | 45 | @Override 46 | public long getId() 47 | { 48 | return id; 49 | } 50 | 51 | @Override 52 | public String getName() 53 | { 54 | return name; 55 | } 56 | 57 | @Override 58 | public String getVersion() 59 | { 60 | return version; 61 | } 62 | 63 | @Override 64 | public Path getDir() 65 | { 66 | return dir; 67 | } 68 | 69 | @Override 70 | public List getAuthors() 71 | { 72 | return authors; 73 | } 74 | 75 | @Override 76 | public String getDescription() 77 | { 78 | return description; 79 | } 80 | 81 | @Override 82 | public String getMcVersion() 83 | { 84 | return mcVersion; 85 | } 86 | 87 | @Override 88 | public String getUrl() 89 | { 90 | return url; 91 | } 92 | 93 | @Override 94 | public String getArtURL() 95 | { 96 | return artUrl; 97 | } 98 | 99 | @Override 100 | public int getMinMemory() 101 | { 102 | return minMemory; 103 | } 104 | 105 | @Override 106 | public int getRecMemory() 107 | { 108 | return recMemory; 109 | } 110 | 111 | public List getFiles() 112 | { 113 | return files; 114 | } 115 | 116 | public List getMods() 117 | { 118 | return files.stream() 119 | .filter(file -> file.getPath().toString().substring(0, 6).contains("mods") && (ModFile.isPotentialMod(file.getName()))) 120 | .map(file -> new ModFile(file.getName(), file.getVersion(), file.getSize(), file.getSha1()).setExists(true)) 121 | .sorted((e1, e2) -> e1.getName().compareToIgnoreCase(e2.getName())) 122 | .collect(Collectors.toList()); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/migration/migrators/V1To2.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.migration.migrators; 2 | 3 | import com.google.common.hash.HashCode; 4 | import com.google.common.hash.HashFunction; 5 | import com.google.common.hash.Hashing; 6 | import net.covers1624.quack.util.HashUtils; 7 | import net.creeperhost.creeperlauncher.Constants; 8 | import net.creeperhost.creeperlauncher.Settings; 9 | import net.creeperhost.creeperlauncher.install.tasks.LocalCache; 10 | import net.creeperhost.creeperlauncher.migration.MigrationContext; 11 | import net.creeperhost.creeperlauncher.migration.Migrator; 12 | import org.apache.logging.log4j.LogManager; 13 | import org.apache.logging.log4j.Logger; 14 | 15 | import java.io.IOException; 16 | import java.nio.file.DirectoryStream; 17 | import java.nio.file.Files; 18 | import java.nio.file.Path; 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | import java.util.UUID; 22 | 23 | /** 24 | * Created by covers1624 on 22/1/21. 25 | */ 26 | @SuppressWarnings ("UnstableApiUsage") 27 | @Migrator.Properties (from = 1, to = 2) 28 | public class V1To2 implements Migrator { 29 | 30 | private static final Logger LOGGER = LogManager.getLogger(); 31 | private static final HashFunction SHA1 = Hashing.sha1(); 32 | 33 | @Override 34 | public void operate(MigrationContext ctx) { 35 | LOGGER.info("Starting migration of local cache."); 36 | Settings.loadSettings(); 37 | Path cacheLocation = Settings.getInstanceLocOr(Constants.INSTANCES_FOLDER_LOC).resolve(".localCache"); 38 | Set files = new HashSet<>(); 39 | try (DirectoryStream stream = Files.newDirectoryStream(cacheLocation)) { 40 | for (Path file : stream) { 41 | String name = file.getFileName().toString(); 42 | if (Files.isRegularFile(file) && !name.endsWith("index.json")) { 43 | try { 44 | UUID.fromString(name);//Try parse name. 45 | files.add(file); 46 | } catch (Exception e) { 47 | LOGGER.warn("Deleting invalid file from cache directory: {}", file); 48 | try { 49 | Files.delete(file); 50 | } catch (IOException ignored) { 51 | } 52 | } 53 | } 54 | } 55 | } catch (IOException ignored) { 56 | } 57 | if (files.isEmpty()) { 58 | LOGGER.info("No files for migration found."); 59 | return; 60 | } 61 | LOGGER.info("Identified {} files for migration.", files.size()); 62 | 63 | try (LocalCache cache = new LocalCache(cacheLocation)) { 64 | cache.disableAutoSave(); 65 | files.parallelStream().forEach(file -> { 66 | boolean error; 67 | try { 68 | HashCode hash = HashUtils.hash(SHA1, file); 69 | error = !cache.ingest(file, hash); 70 | } catch (IOException e) { 71 | error = true; 72 | LOGGER.warn("IO error occurred hashing file.", e); 73 | } 74 | if (error) { 75 | try { 76 | Files.deleteIfExists(file); 77 | } catch (IOException e) { 78 | LOGGER.error("Failed to delete file.", e); 79 | } 80 | } 81 | }); 82 | } 83 | LOGGER.info("Finished migrating files."); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/Profile.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.creeperhost.creeperlauncher.util.GsonUtils; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | import javax.imageio.ImageIO; 9 | import java.awt.image.BufferedImage; 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.nio.file.Path; 13 | import java.util.Base64; 14 | 15 | import static net.creeperhost.creeperlauncher.util.ImageUtils.resizeImage; 16 | 17 | public class Profile 18 | { 19 | private static final Logger LOGGER = LogManager.getLogger(); 20 | 21 | private String name; 22 | private String mcVersion; 23 | private String lastVersionId; 24 | private String lastUsed; 25 | private String type; 26 | private Path gameDir; 27 | private String ID; 28 | private String javaArgs; 29 | private String icon; 30 | private McResolution resolution; 31 | 32 | public Profile(String ID, String name, String mcVersion, String lastVersionId, String lastUsed, String type, Path gameDir, String icon, String args, int ram, int width, int height) 33 | { 34 | this.name = name; 35 | this.mcVersion = mcVersion; 36 | this.lastVersionId = lastVersionId; 37 | if((lastVersionId == null || lastVersionId.isEmpty()) && (mcVersion == null || mcVersion.isEmpty())) this.lastVersionId = mcVersion; 38 | this.lastUsed = lastUsed; 39 | this.type = type; 40 | this.gameDir = gameDir; 41 | this.ID = ID; 42 | if(ram == 0) ram = 1024; 43 | this.javaArgs = ("-Xmx" + ram + "M -Duser.language=en-GB " + args.trim()).trim(); 44 | 45 | if(icon != null && !icon.isEmpty()) { 46 | String[] img = icon.split(","); 47 | byte[] imageByte = img[1].getBytes(); 48 | ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(imageByte)); 49 | try { 50 | BufferedImage art = resizeImage(ImageIO.read(bis), 32, 32); 51 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 52 | ImageIO.write(art, "png", bos); 53 | this.icon = "data:image/png;base64," + Base64.getEncoder().encodeToString(bos.toByteArray()); 54 | } catch (Throwable e) { 55 | LOGGER.warn("Unable to resize pack art for Mojang launcher.", e); 56 | this.icon = icon; 57 | } 58 | } 59 | this.resolution = new McResolution(width, height); 60 | } 61 | 62 | public JsonObject toJsonObject() 63 | { 64 | return GsonUtils.GSON.fromJson(GsonUtils.GSON.toJson(this), JsonObject.class); 65 | } 66 | 67 | public String getName() 68 | { 69 | return name; 70 | } 71 | 72 | public void setName(String name) 73 | { 74 | this.name = name; 75 | } 76 | 77 | public String getType() 78 | { 79 | return type; 80 | } 81 | 82 | public void setType(String type) 83 | { 84 | this.type = type; 85 | } 86 | 87 | public String getID() 88 | { 89 | return ID; 90 | } 91 | 92 | class McResolution 93 | { 94 | int width; 95 | int height; 96 | 97 | public McResolution(int width, int height) 98 | { 99 | this.width = width; 100 | this.height = height; 101 | } 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/Settings.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.reflect.TypeToken; 6 | import net.creeperhost.creeperlauncher.api.WebSocketAPI; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.io.*; 10 | import java.lang.reflect.Type; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.nio.file.StandardOpenOption; 15 | import java.util.HashMap; 16 | 17 | public class Settings 18 | { 19 | private static final Type settingsToken = new TypeToken>(){}.getType(); 20 | private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); 21 | public static HashMap settings = new HashMap<>(); 22 | public static WebSocketAPI webSocketAPI; 23 | 24 | public static void saveSettings() 25 | { 26 | Path json = Constants.BIN_LOCATION.resolve("settings.json"); 27 | if(Files.notExists(Constants.BIN_LOCATION)) 28 | { 29 | try { 30 | Files.createDirectories(Constants.BIN_LOCATION); 31 | } catch(Exception ignored) {} 32 | } 33 | try (BufferedWriter writer = Files.newBufferedWriter(json, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) 34 | { 35 | gson.toJson(settings, settingsToken, writer); 36 | } 37 | catch (IOException ignored) {} 38 | } 39 | 40 | public static void loadSettings() 41 | { 42 | loadSettings(Constants.BIN_LOCATION.resolve("settings.json"), true); 43 | } 44 | 45 | // Only use directly during migrate logic to avoid saving settings immediately 46 | public static void loadSettings(Path json, boolean save) 47 | { 48 | try 49 | { 50 | 51 | if (Files.exists(json)) { 52 | try (BufferedReader reader = Files.newBufferedReader(json)) { 53 | Settings.settings = gson.fromJson(reader, settingsToken); 54 | } 55 | if (Settings.settings.getClass() != HashMap.class) 56 | { 57 | Settings.settings = new HashMap<>(); 58 | } 59 | } else { 60 | Settings.settings = new HashMap<>(); 61 | } 62 | Settings.settings.put("instanceLocation", Settings.settings.getOrDefault("instanceLocation", Constants.INSTANCES_FOLDER_LOC.toAbsolutePath().toString())); 63 | if (save) 64 | { 65 | saveSettings(); 66 | } 67 | } catch (Exception err) 68 | { 69 | Settings.settings = new HashMap<>(); 70 | Settings.settings.put("instanceLocation", Settings.settings.getOrDefault("instanceLocation", Constants.INSTANCES_FOLDER_LOC.toAbsolutePath().toString())); 71 | if (save) 72 | { 73 | saveSettings(); 74 | } 75 | } 76 | } 77 | 78 | public static Path getPathOpt(String opt, Path default_) { 79 | String value = settings.get(opt); 80 | if (StringUtils.isEmpty(value)) { 81 | return default_; 82 | } 83 | return Paths.get(value); 84 | } 85 | 86 | public static Path getInstanceLocOr(Path default_) { 87 | return getPathOpt("instanceLocation", default_); 88 | } 89 | 90 | public static String getDefaultThreadLimit(String arg) 91 | { 92 | int defaultThreads = (Runtime.getRuntime().availableProcessors() / 2) - 1; 93 | if(defaultThreads < 2) defaultThreads = 2; 94 | return String.valueOf(defaultThreads); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/os/platform/window/win/WindowsWindow.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.os.platform.window.win; 2 | 3 | import com.sun.jna.Native; 4 | import com.sun.jna.platform.win32.User32; 5 | import com.sun.jna.platform.win32.WinDef; 6 | import com.sun.jna.platform.win32.WinUser; 7 | import com.sun.jna.ptr.IntByReference; 8 | import net.creeperhost.creeperlauncher.os.platform.window.IMonitor; 9 | import net.creeperhost.creeperlauncher.os.platform.window.IWindow; 10 | 11 | import java.awt.*; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | public class WindowsWindow implements IWindow { 15 | 16 | private final WinDef.HWND hWnd; 17 | private final int pid; 18 | 19 | public WindowsWindow(WinDef.HWND hWnd) { 20 | this.hWnd = hWnd; 21 | IntByReference pidPointer = new IntByReference(); 22 | User32.INSTANCE.GetWindowThreadProcessId(hWnd, pidPointer); 23 | pid = pidPointer.getValue(); 24 | } 25 | 26 | private WinDef.HWND gethWnd() { 27 | return hWnd; 28 | } 29 | 30 | @Override 31 | public Object getHandle() { 32 | return gethWnd(); 33 | } 34 | 35 | public String getWindowTitle() { 36 | char[] windowText = new char[512]; 37 | User32.INSTANCE.GetWindowText(hWnd, windowText, 512); 38 | return Native.toString(windowText); 39 | } 40 | 41 | public void attachedInputAction(Runnable action) { 42 | long foreGround = getPid(); 43 | long us = ProcessHandle.current().pid(); 44 | User32.INSTANCE.AttachThreadInput(new WinDef.DWORD(foreGround), new WinDef.DWORD(us), true); 45 | action.run(); 46 | User32.INSTANCE.AttachThreadInput(new WinDef.DWORD(foreGround), new WinDef.DWORD(us), false); 47 | } 48 | 49 | @Override 50 | public int getPid() { 51 | return pid; 52 | } 53 | 54 | @Override 55 | public Rectangle getRect() { 56 | WinDef.RECT rect = new WinDef.RECT(); 57 | boolean i = User32.INSTANCE.GetWindowRect(hWnd, rect); 58 | if (!i) return new Rectangle(-1, -1, -1, -1); 59 | return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); 60 | } 61 | 62 | @Override 63 | public IMonitor getMonitor() { 64 | WinUser.HMONITOR hMonitorWindow = User32.INSTANCE.MonitorFromWindow(hWnd, WinUser.MONITOR_DEFAULTTONEAREST); 65 | AtomicInteger monitorIndex = new AtomicInteger(); 66 | WinDef.BOOL exists = User32.INSTANCE.EnumDisplayMonitors(null, null, (hMonitor, hdc, rect, lparam) -> { 67 | if (hMonitor.getPointer().equals(hMonitorWindow.getPointer())) { 68 | return 0; 69 | } 70 | monitorIndex.incrementAndGet(); 71 | return 1; 72 | }, new WinDef.LPARAM(0)); 73 | 74 | if (exists.booleanValue()) return null; 75 | 76 | WinUser.MONITORINFO monitorinfo = new WinUser.MONITORINFO(); 77 | 78 | User32.INSTANCE.GetMonitorInfo(hMonitorWindow, monitorinfo); 79 | 80 | int x = monitorinfo.rcMonitor.left; 81 | int y = monitorinfo.rcMonitor.top; 82 | int width = monitorinfo.rcMonitor.right - monitorinfo.rcMonitor.left; 83 | int height = monitorinfo.rcMonitor.bottom - monitorinfo.rcMonitor.top; 84 | return new WindowsMonitor(monitorIndex.get(), new Rectangle(x, y, width, height)); 85 | } 86 | 87 | @Override 88 | public Color getPixelColour(int x, int y) { 89 | WinDef.HWND hwnd = User32.INSTANCE.GetDesktopWindow(); 90 | 91 | WinDef.HDC hdc = User32.INSTANCE.GetDC(hwnd); 92 | int i = WindowsWindowHelper.OurGDI32.INSTANCE.GetPixel(hdc, x, y); 93 | User32.INSTANCE.ReleaseDC(hwnd, hdc); 94 | return new Color(i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff); 95 | } 96 | 97 | @Override 98 | public void bringToFront() { 99 | attachedInputAction(() -> { 100 | User32.INSTANCE.SetFocus(hWnd); 101 | User32.INSTANCE.SetForegroundWindow(hWnd); 102 | }); 103 | } 104 | 105 | @Override 106 | public boolean hasFocus() { 107 | return false; 108 | } 109 | 110 | @Override 111 | public boolean isForeground() { 112 | return false; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/handlers/instances/SyncInstanceHandler.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api.handlers.instances; 2 | 3 | import net.creeperhost.creeperlauncher.*; 4 | import net.creeperhost.creeperlauncher.api.data.other.CloseModalData; 5 | import net.creeperhost.creeperlauncher.api.data.instances.InstallInstanceData; 6 | import net.creeperhost.creeperlauncher.api.data.other.OpenModalData; 7 | import net.creeperhost.creeperlauncher.api.handlers.IMessageHandler; 8 | import net.creeperhost.minetogether.lib.cloudsaves.CloudSaveManager; 9 | import net.creeperhost.creeperlauncher.minecraft.GameLauncher; 10 | import net.creeperhost.creeperlauncher.minecraft.McUtils; 11 | import net.creeperhost.creeperlauncher.minecraft.modloader.ModLoader; 12 | import net.creeperhost.creeperlauncher.minecraft.modloader.ModLoaderManager; 13 | import net.creeperhost.creeperlauncher.os.OS; 14 | import net.creeperhost.creeperlauncher.os.Platform; 15 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 16 | import net.creeperhost.creeperlauncher.util.FileUtils; 17 | 18 | import java.io.FileNotFoundException; 19 | import java.nio.file.Files; 20 | import java.nio.file.Path; 21 | import java.util.List; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.atomic.AtomicReference; 24 | 25 | public class SyncInstanceHandler implements IMessageHandler 26 | { 27 | public static AtomicReference lastError = new AtomicReference(); 28 | 29 | @Override 30 | public void handle(InstallInstanceData data) 31 | { 32 | if(data.uuid != null && data.uuid.length() > 0) { 33 | if (CreeperLauncher.isInstalling.get()) { 34 | Settings.webSocketAPI.sendMessage(new InstallInstanceData.Reply(data, "error", "Install in progress.", CreeperLauncher.currentInstall.get().currentUUID)); 35 | return; 36 | } 37 | Settings.webSocketAPI.sendMessage(new InstallInstanceData.Reply(data, "init", "Install started.", data.uuid)); 38 | //Create the folder 39 | Path instanceDir = Constants.INSTANCES_FOLDER_LOC.resolve(data.uuid); 40 | Path instanceJson = instanceDir.resolve("instance.json"); 41 | FileUtils.createDirectories(instanceDir); 42 | 43 | //Download the instance.json from the s3Bucket 44 | try { 45 | CloudSaveManager.downloadFile(data.uuid + "/instance.json", instanceJson, true, null); 46 | } catch (Exception ignored) {} 47 | 48 | LocalInstance instance; 49 | try { 50 | instance = new LocalInstance(instanceDir); 51 | instance.cloudSync(true); 52 | 53 | List modLoaders = ModLoaderManager.getModLoaders(McUtils.getTargets(instance.getDir())); 54 | if (modLoaders.size() != 1) { 55 | throw new RuntimeException("Only one mod loader is currently supported!"); 56 | } else { 57 | CompletableFuture.runAsync(() -> 58 | { 59 | OpenModalData.openModal("Preparing environment", "Installing Mod Loaders
", List.of()); 60 | ModLoader modLoader = modLoaders.get(0); 61 | modLoader.install(instance); 62 | Platform platform = OS.CURRENT.getPlatform(); 63 | if (Files.notExists(platform.getLauncherExecutable())) { 64 | OpenModalData.openModal("Preparing environment", "Installing Minecraft Launcher
", List.of()); 65 | 66 | platform.installLauncher(); 67 | if (!Files.exists(Constants.LAUNCHER_PROFILES_JSON)) GameLauncher.downloadLauncherProfiles(); 68 | } 69 | }).thenRun(() -> 70 | { 71 | Settings.webSocketAPI.sendMessage(new InstallInstanceData.Reply(data, "success", "Install complete.", data.uuid)); 72 | Settings.webSocketAPI.sendMessage(new CloseModalData()); 73 | }); 74 | } 75 | } catch (FileNotFoundException e) { 76 | Settings.webSocketAPI.sendMessage(new InstallInstanceData.Reply(data, "error", lastError.get(), data.uuid)); 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/modloader/fabric/FabricModLoader.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft.modloader.fabric; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonObject; 5 | import net.creeperhost.creeperlauncher.Constants; 6 | import net.creeperhost.creeperlauncher.minecraft.modloader.ModLoader; 7 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 8 | import net.creeperhost.creeperlauncher.util.*; 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | 12 | import java.io.IOException; 13 | import java.net.URL; 14 | import java.nio.charset.StandardCharsets; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.util.Date; 18 | import java.util.List; 19 | 20 | public class FabricModLoader extends ModLoader 21 | { 22 | private static final Logger LOGGER = LogManager.getLogger(); 23 | 24 | private static final String FABRIC_MET_URL = "https://meta.fabricmc.net/v2/"; 25 | private static final String FABRIC_MAVEN_URL = "https://maven.fabricmc.net/"; 26 | 27 | public FabricModLoader(List loaderTargets) 28 | { 29 | super(loaderTargets); 30 | } 31 | 32 | @Override 33 | public String getName() 34 | { 35 | return "fabric"; 36 | } 37 | 38 | @Override 39 | public Path install(LocalInstance instance) 40 | { 41 | LOGGER.info("Minecraft version: {} Fabric version: {}", getMinecraftVersion(), getFabricVersion()); 42 | var profileName = String.format("fabric-loader-%s-%s", getFabricVersion(), getMinecraftVersion()); 43 | 44 | instance.modLoader = profileName; 45 | 46 | JsonObject loaderMeta; 47 | try 48 | { 49 | loaderMeta = getInstallMeta(); 50 | } catch (IOException e) { 51 | LOGGER.error("Failed to get fabric install meta", e); 52 | return null; 53 | } 54 | 55 | var launcherMeta = loaderMeta.get("launcherMeta").getAsJsonObject(); 56 | var libraries = (JsonArray) launcherMeta.get("libraries").getAsJsonObject().get("common"); 57 | 58 | //Add intermediary and fabric-loader as those are not included in the metadata 59 | libraries.add(getLibrary("net.fabricmc:intermediary:" + getMinecraftVersion(), FABRIC_MAVEN_URL)); 60 | libraries.add(getLibrary("net.fabricmc:fabric-loader:" + getFabricVersion(), FABRIC_MAVEN_URL)); 61 | 62 | var versionsDir = Constants.VERSIONS_FOLDER_LOC; 63 | var profileDir = versionsDir.resolve(profileName); 64 | var profileJson = profileDir.resolve(profileName + ".json"); 65 | 66 | FileUtils.createDirectories(profileDir); 67 | 68 | Path dummyJar = profileDir.resolve(profileName + ".jar"); 69 | try 70 | { 71 | if (!Files.exists(dummyJar)) 72 | { 73 | Files.createFile(dummyJar); 74 | } 75 | } catch (IOException e) { 76 | LOGGER.error("Failed to create fabric jar, is the game running?", e); 77 | return null; 78 | } 79 | 80 | var currentTime = MiscUtils.ISO_8601.format(new Date()); 81 | 82 | JsonObject profile = new JsonObject(); 83 | profile.addProperty("id", profileName); 84 | profile.addProperty("inheritsFrom", getMinecraftVersion()); 85 | profile.addProperty("releaseTime", currentTime); 86 | profile.addProperty("time", currentTime); 87 | profile.addProperty("type", "release"); 88 | 89 | profile.addProperty("mainClass", launcherMeta.get("mainClass").getAsJsonObject().get("client").getAsString()); 90 | 91 | var arguments = new JsonObject(); 92 | arguments.add("game", new JsonArray()); 93 | profile.add("arguments", arguments); 94 | 95 | profile.add("libraries", libraries); 96 | 97 | try 98 | { 99 | Files.write(profileJson, GsonUtils.GSON.toJson(profile).getBytes(StandardCharsets.UTF_8)); 100 | } catch (IOException e) { 101 | LOGGER.error("Failed to create fabric profile json, is the game running?", e); 102 | return null; 103 | } 104 | 105 | return null; 106 | } 107 | 108 | private JsonObject getInstallMeta() throws IOException 109 | { 110 | var metaUrl = String.format("%sversions/loader/%s/%s", FABRIC_MET_URL, getMinecraftVersion(), getFabricVersion()); 111 | var meta = DownloadUtils.urlToString(new URL(metaUrl)); 112 | return GsonUtils.GSON.fromJson(meta, JsonObject.class); 113 | } 114 | 115 | private JsonObject getLibrary(String mavenPath, String url) 116 | { 117 | var jsonObject = new JsonObject(); 118 | jsonObject.addProperty("name", mavenPath); 119 | jsonObject.addProperty("url", url); 120 | return jsonObject; 121 | } 122 | 123 | public String getFabricVersion() 124 | { 125 | return getTargetVersion("fabric").orElseThrow(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/modloader/forge/ForgeUniversalModLoader.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft.modloader.forge; 2 | 3 | import net.creeperhost.creeperlauncher.Constants; 4 | import net.creeperhost.creeperlauncher.api.DownloadableFile; 5 | import net.creeperhost.creeperlauncher.install.tasks.DownloadTask; 6 | import net.creeperhost.creeperlauncher.minecraft.McUtils; 7 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 8 | import net.creeperhost.creeperlauncher.util.FileUtils; 9 | import net.creeperhost.creeperlauncher.util.ForgeUtils; 10 | import net.creeperhost.creeperlauncher.util.LoaderTarget; 11 | import org.apache.logging.log4j.LogManager; 12 | import org.apache.logging.log4j.Logger; 13 | 14 | import java.net.URI; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.nio.file.StandardCopyOption; 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | public class ForgeUniversalModLoader extends ForgeModLoader 23 | { 24 | private static final Logger LOGGER = LogManager.getLogger(); 25 | 26 | public ForgeUniversalModLoader(List loaderTargets) 27 | { 28 | super(loaderTargets); 29 | } 30 | 31 | @Override 32 | public String getName() 33 | { 34 | return "forge"; 35 | } 36 | 37 | @Override 38 | public Path install(LocalInstance instance) 39 | { 40 | Path returnFile = null; 41 | String newname = getMinecraftVersion() + "-forge" + getMinecraftVersion() + "-" + getForgeVersion(); 42 | instance.modLoader = newname; 43 | LOGGER.info("Minecraft version: {} Forge version: {}", getMinecraftVersion(), getForgeVersion()); 44 | Path file = Constants.VERSIONS_FOLDER_LOC.resolve(newname); 45 | FileUtils.createDirectories(file); 46 | 47 | try 48 | { 49 | URI url = ForgeUtils.findForgeDownloadURL(getMinecraftVersion(), getForgeVersion()); 50 | Path forgeFile = file.resolve(newname + ".jar"); 51 | DownloadableFile forge = new DownloadableFile(getForgeVersion(), forgeFile, url.toString(), Collections.emptyList(), 0, 0, newname, "modloader", String.valueOf(System.currentTimeMillis() / 1000L)); 52 | DownloadTask task = new DownloadTask(forge, forgeFile); 53 | task.execute().join(); 54 | 55 | LOGGER.info("Completed download of {}", newname); 56 | 57 | if (Files.exists(forgeFile)) 58 | { 59 | boolean extracted = ForgeUtils.extractJson(forgeFile, newname + ".json"); 60 | Path forgeJson = file.resolve(newname + ".json"); 61 | if(!extracted) 62 | { 63 | LOGGER.error("Failed to extract version json, attempting to download it from repo"); 64 | String downloadName = "forge-" + getMinecraftVersion() + ".json"; 65 | DownloadableFile fjson = new DownloadableFile(forgeJson.getFileName().toString(), forgeJson, "https://apps.modpacks.ch/versions/minecraftjsons/" + downloadName, Collections.emptyList(), 0, 0, downloadName, "modloader", String.valueOf(System.currentTimeMillis() / 1000L)); 66 | DownloadTask ftask = new DownloadTask(fjson, forgeJson); 67 | ftask.execute().join(); 68 | } 69 | if (Files.exists(forgeJson)) 70 | { 71 | ForgeUtils.updateForgeJson(forgeJson, newname, getMinecraftVersion()); 72 | //Move the forge jar to its home in libs 73 | Path libForgeDir = Constants.LIBRARY_LOCATION.resolve("net/minecraftforge/forge/" + getMinecraftVersion() + "-" + getForgeVersion()); 74 | FileUtils.createDirectories(libForgeDir); 75 | Path forgeLib = libForgeDir.resolve("forge-" + getMinecraftVersion() + "-" + getForgeVersion() + ".jar"); 76 | if (!Files.exists(forgeLib)) Files.copy(forgeFile, forgeLib, StandardCopyOption.REPLACE_EXISTING); 77 | 78 | returnFile = forgeJson; 79 | } else 80 | { 81 | LOGGER.error("Failed to get the 'version.json' for '{}'", newname); 82 | } 83 | } 84 | } catch (Throwable e) 85 | { 86 | LOGGER.error(e); 87 | } 88 | return returnFile; 89 | } 90 | 91 | @Override 92 | public boolean isApplicable() 93 | { 94 | int minorMcVersion = McUtils.parseMinorVersion(getTargetVersion("minecraft").orElse("0.0.0")); 95 | 96 | if (minorMcVersion == 12) { 97 | String[] versions = getForgeVersion().split("\\."); 98 | try { 99 | int forgeVer = Integer.parseInt(versions[versions.length - 1]); 100 | return super.isApplicable() && forgeVer < 2851; 101 | } catch (NumberFormatException ignored) { 102 | // ¯\_(ツ)_/¯ 103 | } 104 | } 105 | //1.6 -> 1.12.2 106 | return super.isApplicable() && minorMcVersion >= 6 && minorMcVersion <= 12; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/util/LogsUploader.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.util; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import net.creeperhost.creeperlauncher.Constants; 6 | import net.creeperhost.creeperlauncher.os.OS; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | import org.apache.logging.log4j.core.Appender; 10 | import org.apache.logging.log4j.core.LoggerContext; 11 | import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.io.IOException; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | 18 | /** 19 | * Created by covers1624 on 24/2/21. 20 | */ 21 | public class LogsUploader { 22 | 23 | private static final Logger LOGGER = LogManager.getLogger(); 24 | 25 | /** 26 | * Uploads the UI and Backend Debug logs to pste.ch 27 | * 28 | * @param uiVersion The UI Version. 29 | * @param frontendLogs The Frontend logs. 30 | * @return The pste.ch code, or null if an error occurred. 31 | */ 32 | @Nullable 33 | public static String uploadUILogs(@Nullable String uiVersion, @Nullable String frontendLogs) { 34 | String debugLogs = getDebugLog(); 35 | String uploadData = "UI Version: " + (uiVersion != null ? uiVersion : "Unknown") + "\n" + 36 | "App Version: " + Constants.APPVERSION + "\n" + 37 | "Platform: " + Constants.PLATFORM + "\n" + 38 | "Operating System: " + OS.CURRENT + "\n" + 39 | "\n" + 40 | "\n" + 41 | padString(" debug.log ") + "\n" + 42 | (debugLogs == null ? "Not available" : debugLogs) + "\n" + 43 | "\n" + 44 | "\n" + 45 | padString(" main.log ") + "\n" + 46 | (frontendLogs == null ? "Not available" : frontendLogs); 47 | 48 | return uploadPaste(uploadData); 49 | } 50 | 51 | /** 52 | * Reads the latest backend debug.log file from disk. 53 | * 54 | * @return The string content, or null if an error occurred. 55 | */ 56 | @Nullable 57 | public static String getDebugLog() { 58 | try { 59 | LoggerContext context = (LoggerContext) LogManager.getContext(); 60 | for (org.apache.logging.log4j.core.Logger logger : context.getLoggers()) { 61 | for (Appender appender : logger.getAppenders().values()) { 62 | if (appender instanceof AbstractOutputStreamAppender) { 63 | ((AbstractOutputStreamAppender) appender).getManager().flush(); 64 | } 65 | } 66 | } 67 | } catch (Throwable ignored) { 68 | } 69 | 70 | Path debugLogFile = Constants.getDataDir().resolve("logs/debug.log"); 71 | 72 | if (Files.exists(debugLogFile)) { 73 | try { 74 | return Files.readString(debugLogFile); 75 | } catch (IOException e) { 76 | LOGGER.warn("Failed to load debug logs.", e); 77 | } 78 | } 79 | return null; 80 | } 81 | 82 | /** 83 | * Uploads the given data to pste.ch 84 | * 85 | * @param data The data to upload. 86 | * @return The pste.ch code, or null if an error occured. 87 | */ 88 | @Nullable 89 | public static String uploadPaste(@Nullable String data) { 90 | String result = WebUtils.postWebResponse("https://pste.ch/documents", data == null ? "No data available." : data, "text/plain; charset=UTF-8"); 91 | try { 92 | JsonObject objResponse = GsonUtils.GSON.fromJson(result, JsonObject.class); 93 | JsonElement key = objResponse.get("key"); 94 | if (key == null || !key.isJsonPrimitive()) return null; 95 | 96 | return key.getAsString(); 97 | } catch (Throwable e) { 98 | return null; 99 | } 100 | } 101 | 102 | private static String padString(String stringToPad) { 103 | int desiredLength = 86; 104 | char padChar = '='; 105 | int strLen = stringToPad.length(); 106 | float halfLength = ((float) desiredLength - (float) strLen) / (float) 2; 107 | int leftPad; 108 | int rightPad; 109 | if (((int) halfLength) != halfLength) { 110 | leftPad = (int) halfLength + 1; 111 | rightPad = (int) halfLength; 112 | } else { 113 | leftPad = rightPad = (int) halfLength; 114 | } 115 | 116 | String padCharStr = String.valueOf(padChar); 117 | 118 | return padCharStr.repeat(leftPad).concat(stringToPad).concat(padCharStr.repeat(rightPad)); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/api/WebSocketAPI.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.api; 2 | 3 | import java.net.BindException; 4 | import net.creeperhost.creeperlauncher.Constants; 5 | import net.creeperhost.creeperlauncher.CreeperLauncher; 6 | import net.creeperhost.creeperlauncher.Settings; 7 | import net.creeperhost.creeperlauncher.api.data.BaseData; 8 | import net.creeperhost.creeperlauncher.util.GsonUtils; 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | import org.java_websocket.WebSocket; 12 | import org.java_websocket.handshake.ClientHandshake; 13 | import org.java_websocket.server.WebSocketServer; 14 | 15 | import java.net.InetAddress; 16 | import java.net.InetSocketAddress; 17 | import java.nio.ByteBuffer; 18 | import java.util.Random; 19 | import java.util.UUID; 20 | import java.util.concurrent.ConcurrentLinkedQueue; 21 | 22 | public class WebSocketAPI extends WebSocketServer 23 | { 24 | private static final Logger LOGGER = LogManager.getLogger(); 25 | 26 | private boolean fullyConnected = false; 27 | 28 | public WebSocketAPI(InetSocketAddress address) 29 | { 30 | super(address); 31 | } 32 | 33 | int connections = 0; 34 | 35 | private static final ConcurrentLinkedQueue notConnectedQueue = new ConcurrentLinkedQueue<>(); 36 | 37 | public static Random random = new Random(); 38 | 39 | public static int generateRandomPort() { 40 | return random.nextInt(9999) + 10000; 41 | } 42 | 43 | public static String generateSecret() { 44 | return UUID.randomUUID().toString(); 45 | } 46 | 47 | @Override 48 | public void onOpen(WebSocket conn, ClientHandshake handshake) 49 | { 50 | if (CreeperLauncher.defaultWebsocketPort && !CreeperLauncher.isDevMode) 51 | { 52 | conn.send("{\"port\": \"" + CreeperLauncher.websocketPort + "\", \"secret\": \"" + CreeperLauncher.websocketSecret + "\"}"); 53 | conn.close(); 54 | LOGGER.info("Front end connected: {} - sending our socket and secret and relaunching websocket", conn.getRemoteSocketAddress()); 55 | CreeperLauncher.defaultWebsocketPort = false; 56 | Settings.webSocketAPI = new WebSocketAPI(new InetSocketAddress(InetAddress.getLoopbackAddress(), CreeperLauncher.websocketPort)); 57 | Settings.webSocketAPI.start(); 58 | try { 59 | stop(); 60 | } catch (Exception ignored) {} 61 | return; 62 | } else { 63 | CreeperLauncher.websocketDisconnect = false; 64 | } 65 | 66 | LOGGER.info("Front end connected: {}", conn.getRemoteSocketAddress()); 67 | fullyConnected = true; 68 | connections++; 69 | notConnectedQueue.forEach(conn::send); 70 | notConnectedQueue.clear(); 71 | } 72 | 73 | @Override 74 | public void onClose(WebSocket conn, int code, String reason, boolean remote) 75 | { 76 | connections--; 77 | if (connections == 0) CreeperLauncher.websocketDisconnect = true; 78 | LOGGER.info("closed {} with exit code {} additional info: {}", conn.getRemoteSocketAddress(), code, reason); 79 | } 80 | 81 | @Override 82 | public void onMessage(WebSocket conn, String message) 83 | { 84 | WebSocketMessengerHandler.handleMessage(message); 85 | } 86 | 87 | @Override 88 | public void onMessage(WebSocket conn, ByteBuffer message) 89 | { 90 | } 91 | 92 | @Override 93 | public void onError(WebSocket conn, Exception ex) 94 | { 95 | try 96 | { 97 | CreeperLauncher.websocketDisconnect = true; 98 | LOGGER.error("an error occurred on connection {}", conn.getRemoteSocketAddress(), ex); 99 | } catch (NullPointerException ignored) 100 | { 101 | if(ex instanceof BindException) { 102 | CreeperLauncher.websocketPort = generateRandomPort(); 103 | LOGGER.info("New Port: {}", CreeperLauncher.websocketPort); 104 | Settings.webSocketAPI = new WebSocketAPI(new InetSocketAddress(InetAddress.getLoopbackAddress(), CreeperLauncher.websocketPort)); 105 | Settings.webSocketAPI.start(); 106 | try { 107 | stop(); 108 | } catch (Exception ignored1) {} 109 | } 110 | } 111 | } 112 | 113 | @Override 114 | public void onStart() 115 | { 116 | LOGGER.info("Server started successfully - {}", Constants.APPVERSION); 117 | } 118 | 119 | // TODO: ensure thread safety 120 | public void sendMessage(BaseData data) 121 | { 122 | String s = GsonUtils.GSON.toJson(data); 123 | if (getConnections().isEmpty() || !fullyConnected) 124 | { 125 | notConnectedQueue.add(s); 126 | } else { 127 | getConnections().forEach((client) -> client.send(s)); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/install/tasks/http/OkHttpClientImpl.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.install.tasks.http; 2 | 3 | import com.google.common.hash.HashFunction; 4 | import com.google.common.hash.Hasher; 5 | import net.covers1624.quack.util.SneakyUtils; 6 | import okhttp3.*; 7 | import okio.*; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.io.IOException; 11 | import java.nio.file.Path; 12 | 13 | public class OkHttpClientImpl implements IHttpClient 14 | { 15 | private static final OkHttpClient client; 16 | 17 | static { 18 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 19 | builder.addInterceptor((chain) -> 20 | { 21 | Request request = chain.request(); 22 | Response originalResponse = chain.proceed(request); 23 | return originalResponse.newBuilder() 24 | .body(new ProgressResponseBody(originalResponse.body(), request.tag(ResponseHandlers.class), request.tag(Long.class))) 25 | .build(); 26 | }); 27 | client = builder.build(); 28 | } 29 | 30 | @Override 31 | public String makeRequest(String url) 32 | { 33 | return null; 34 | } 35 | 36 | /* returns when done */ 37 | @Override 38 | public DownloadedFile doDownload(String url, Path destination, IProgressUpdater progressWatcher, HashFunction hashFunc, long maxSpeed) throws IOException 39 | { 40 | Hasher hasher = hashFunc.newHasher(); 41 | ResponseHandlers responseHandlers = new ResponseHandlers(progressWatcher, e -> hasher.putBytes(e.readByteArray())); 42 | 43 | Request request = new Request.Builder() 44 | .url(url) 45 | .tag(Long.class, maxSpeed) 46 | .tag(ResponseHandlers.class, responseHandlers) 47 | .build(); 48 | 49 | Response response = client.newCall(request).execute(); 50 | 51 | BufferedSink sink = Okio.buffer(Okio.sink(destination)); 52 | sink.writeAll(response.body().source()); 53 | sink.close(); 54 | 55 | response.close(); 56 | 57 | return new DownloadedFile(destination, 0, hasher.hash()); 58 | } 59 | 60 | private static class ProgressResponseBody extends ResponseBody 61 | { 62 | 63 | private final Throttler speed = new Throttler(); 64 | private final ResponseBody responseBody; 65 | private final ResponseHandlers responseHandlers; 66 | private final long maxSpeed; 67 | private BufferedSource bufferedSource; 68 | 69 | public ProgressResponseBody(ResponseBody responseBody, ResponseHandlers progressListener, long maxSpeed) 70 | { 71 | this.responseBody = responseBody; 72 | this.responseHandlers = progressListener; 73 | this.maxSpeed = maxSpeed; 74 | } 75 | 76 | @Override 77 | public MediaType contentType() 78 | { 79 | return responseBody.contentType(); 80 | } 81 | 82 | @Override 83 | public long contentLength() 84 | { 85 | return responseBody.contentLength(); 86 | } 87 | 88 | @Override 89 | public BufferedSource source() 90 | { 91 | if (bufferedSource == null) 92 | { 93 | bufferedSource = Okio.buffer(source(responseBody.source())); 94 | } 95 | return bufferedSource; 96 | } 97 | 98 | private Source source(Source source) 99 | { 100 | if (maxSpeed > 0) 101 | { 102 | speed.bytesPerSecond((maxSpeed / 8) * 2);//Some reason it always limits to 50% for me, so we multiply! 103 | } else 104 | { 105 | speed.bytesPerSecond(Long.MAX_VALUE); 106 | } 107 | return new ForwardingSource(speed.source(source)) 108 | { 109 | long totalBytesRead = 0L; 110 | 111 | @Override 112 | public long read(@NotNull Buffer sink, long byteCount) throws IOException 113 | { 114 | long bytesRead = super.read(sink, byteCount); 115 | responseHandlers.handler.accept(sink.peek()); 116 | 117 | totalBytesRead += bytesRead != -1 ? bytesRead : 0; 118 | 119 | long totalBytes = responseBody.contentLength(); 120 | 121 | if (responseHandlers.updater != null) 122 | responseHandlers.updater.update(totalBytesRead, bytesRead, totalBytes, totalBytes == totalBytesRead); 123 | 124 | return bytesRead; 125 | } 126 | }; 127 | } 128 | } 129 | 130 | private static class ResponseHandlers 131 | { 132 | private final IProgressUpdater updater; 133 | private final SneakyUtils.ThrowingConsumer handler; 134 | 135 | public ResponseHandlers(IProgressUpdater updater, SneakyUtils.ThrowingConsumer handler) 136 | { 137 | this.updater = updater; 138 | this.handler = handler; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/Constants.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher; 2 | 3 | import net.creeperhost.creeperlauncher.os.OS; 4 | import net.creeperhost.creeperlauncher.os.OSUtils; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | 11 | public class Constants 12 | { 13 | private static final Logger LOGGER = LogManager.getLogger(); 14 | 15 | //CWD 16 | public static final Path WORKING_DIR = Paths.get(System.getProperty("user.dir")); 17 | private static final String INNER_DATA_DIR = ".ftba"; 18 | private static final Path DATA_DIR = Paths.get(System.getProperty("user.home"), INNER_DATA_DIR); 19 | 20 | //Launcher titles 21 | public static final String windowTitle = "FTBApp"; 22 | 23 | //Mojang 24 | public static final String MC_VERSION_MANIFEST = "https://launchermeta.mojang.com/mc/game/version_manifest.json"; 25 | public static final String MC_RESOURCES = "http://resources.download.minecraft.net/"; 26 | public static final String MC_LIBS = "https://libraries.minecraft.net/"; 27 | public static final String MC_LAUNCHER = "https://launcher.mojang.com/download/Minecraft."; 28 | 29 | //API 30 | public static final String CREEPERHOST_MODPACK = CreeperLauncher.isDevMode ? "https://modpack-api.ch.tools" : "https://api.modpacks.ch"; 31 | public static final String CREEPERHOST_MODPACK_SEARCH2 = CREEPERHOST_MODPACK + "/public/modpack/"; 32 | public static final String SHARE_API = CREEPERHOST_MODPACK + Constants.KEY + "/modpack/share/"; 33 | public static final String MOD_API = CREEPERHOST_MODPACK + "/public/mod/"; 34 | 35 | //Forge 36 | public static final String FORGE_XML = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml"; 37 | public static final String FORGE_MAVEN = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/"; 38 | public static final String FORGE_RECOMMENDED = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json"; 39 | public static final String FORGE_CH = "https://maven.creeperhost.net/net/minecraftforge/forge/"; 40 | 41 | //Paths 42 | public static final Path BIN_LOCATION_OURS = WORKING_DIR.resolve("bin"); 43 | public static final Path BIN_LOCATION = getDataDir().resolve("bin"); 44 | 45 | public static final Path VERSIONS_FOLDER_LOC = getDataDir().resolve(Paths.get("bin", "versions")); 46 | public static final Path INSTANCES_FOLDER_LOC = getDataDir().resolve("instances"); 47 | public static final String LAUNCHER_PROFILES_JSON_NAME = "launcher_profiles.json"; 48 | public static final Path LAUNCHER_PROFILES_JSON = BIN_LOCATION.resolve(LAUNCHER_PROFILES_JSON_NAME); 49 | public static final Path LIBRARY_LOCATION = BIN_LOCATION.resolve("libraries"); 50 | public static final Path OLD_CACHE_LOCATION = getDataDir().resolve(".localCache"); 51 | 52 | //Other 53 | public static final int WEBSOCKET_PORT = 13377; 54 | public static final String APPVERSION = "@APPVERSION@"; 55 | public static final String BRANCH = "@BRANCH@"; 56 | public static final String PLATFORM = WORKING_DIR.toAbsolutePath().toString().contains("Overwolf") ? "Overwolf" : "Electron"; 57 | 58 | //Auth 59 | public static String KEY = ""; 60 | public static String SECRET = ""; 61 | 62 | //MT Identifiers 63 | public static String MT_HASH = ""; 64 | public static Path MTCONNECT_DIR = getDataDir().resolve("MTConnect"); 65 | 66 | //S3 Auth 67 | public static String S3_KEY = ""; 68 | public static String S3_SECRET = ""; 69 | public static String S3_BUCKET = ""; 70 | public static String S3_HOST = ""; 71 | 72 | 73 | public static String getCreeperhostModpackSearch2(boolean _private, byte packType) 74 | { 75 | String typeSlug = "modpack"; 76 | 77 | switch (packType) 78 | { 79 | case 1: 80 | return Constants.CREEPERHOST_MODPACK + "/public/" + "curseforge" + "/"; // no such thing as private curse modpacks, rest of logic not needed 81 | } 82 | if(Constants.KEY.isEmpty() || !_private) 83 | { 84 | return Constants.CREEPERHOST_MODPACK + "/public/" + typeSlug + "/"; 85 | } 86 | if(Constants.KEY.isEmpty() && _private) 87 | { 88 | LOGGER.error("Tried to access a private pack without having configured the secret and key."); 89 | } 90 | return Constants.CREEPERHOST_MODPACK + "/" + Constants.KEY + "/" + typeSlug + "/"; 91 | } 92 | public static Path getDataDir() 93 | { 94 | Path ret = DATA_DIR; 95 | switch (OS.CURRENT) { 96 | case WIN: 97 | ret = Paths.get(System.getenv("LOCALAPPDATA"), INNER_DATA_DIR); 98 | break; 99 | case MAC: 100 | ret = Paths.get(System.getProperty("user.home"), "Library", "Application Support", INNER_DATA_DIR); 101 | break; 102 | } 103 | return ret.toAbsolutePath().normalize(); 104 | } 105 | 106 | public static Path getDataDirOld() { 107 | return DATA_DIR.toAbsolutePath(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/minecraft/StartJson.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.minecraft; 2 | 3 | public class StartJson 4 | { 5 | String id; 6 | String time; 7 | String releaseType; 8 | String minecraftArguments; 9 | String mainClass; 10 | String inheritsFrom; 11 | String jar; 12 | Libraries[] libraries; 13 | 14 | public StartJson(String id, String time, String releaseType, String minecraftArguments, String mainClass, String inheritsFrom, String jar, Libraries[] libraries) 15 | { 16 | this.id = id; 17 | this.time = time; 18 | this.releaseType = releaseType; 19 | this.minecraftArguments = minecraftArguments; 20 | this.mainClass = mainClass; 21 | this.inheritsFrom = inheritsFrom; 22 | this.jar = jar; 23 | this.libraries = libraries; 24 | } 25 | 26 | public String getId() 27 | { 28 | return id; 29 | } 30 | 31 | public String getTime() 32 | { 33 | return time; 34 | } 35 | 36 | public String getReleaseType() 37 | { 38 | return releaseType; 39 | } 40 | 41 | public String getMinecraftArguments() 42 | { 43 | return minecraftArguments; 44 | } 45 | 46 | public String getMainClass() 47 | { 48 | return mainClass; 49 | } 50 | 51 | public String getInheritsFrom() 52 | { 53 | return inheritsFrom; 54 | } 55 | 56 | public String getJar() 57 | { 58 | return jar; 59 | } 60 | 61 | public void setId(String id) 62 | { 63 | this.id = id; 64 | } 65 | 66 | public void setJar(String jar) 67 | { 68 | this.jar = jar; 69 | } 70 | 71 | public void setInheritsFrom(String inheritsFrom) 72 | { 73 | this.inheritsFrom = inheritsFrom; 74 | } 75 | 76 | public void setMainClass(String mainClass) 77 | { 78 | this.mainClass = mainClass; 79 | } 80 | 81 | public void setMinecraftArguments(String minecraftArguments) 82 | { 83 | this.minecraftArguments = minecraftArguments; 84 | } 85 | 86 | public void setReleaseType(String releaseType) 87 | { 88 | this.releaseType = releaseType; 89 | } 90 | 91 | public void setTime(String time) 92 | { 93 | this.time = time; 94 | } 95 | 96 | public void setLibraries(Libraries[] libraries) 97 | { 98 | this.libraries = libraries; 99 | } 100 | 101 | public Libraries[] getLibraries() 102 | { 103 | return libraries; 104 | } 105 | 106 | public static class Libraries 107 | { 108 | String name; 109 | String url; 110 | boolean serverreq; 111 | boolean clientreq; 112 | Natives natives; 113 | Rules[] rule; 114 | 115 | public Libraries(String name, String url, boolean serverreq, boolean clientreq, Natives natives, Rules[] rule) 116 | { 117 | this.name = name; 118 | this.url = url; 119 | this.serverreq = serverreq; 120 | this.clientreq = clientreq; 121 | this.natives = natives; 122 | this.rule = rule; 123 | } 124 | 125 | public String getName() 126 | { 127 | return name; 128 | } 129 | 130 | public String getUrl() 131 | { 132 | return url; 133 | } 134 | 135 | public boolean isClientReq() 136 | { 137 | return clientreq; 138 | } 139 | 140 | public boolean isServerReq() 141 | { 142 | return serverreq; 143 | } 144 | 145 | public Natives getNatives() 146 | { 147 | return natives; 148 | } 149 | 150 | public Rules[] getRule() 151 | { 152 | return rule; 153 | } 154 | } 155 | 156 | public class Rules 157 | { 158 | String action; 159 | Os os; 160 | 161 | public Rules(String action, Os os1) 162 | { 163 | this.action = action; 164 | this.os = os1; 165 | } 166 | 167 | public Os getOs() 168 | { 169 | return os; 170 | } 171 | 172 | public String getAction() 173 | { 174 | return action; 175 | } 176 | } 177 | 178 | public class Os 179 | { 180 | String name; 181 | 182 | public Os(String name) 183 | { 184 | this.name = name; 185 | } 186 | 187 | public String getName() 188 | { 189 | return name; 190 | } 191 | } 192 | 193 | public class Natives 194 | { 195 | String linux; 196 | String windows; 197 | String osx; 198 | 199 | public Natives(String linux, String windows, String osx) 200 | { 201 | this.linux = linux; 202 | this.windows = windows; 203 | this.osx = osx; 204 | } 205 | 206 | 207 | public String getLinux() 208 | { 209 | return linux; 210 | } 211 | 212 | public String getOsx() 213 | { 214 | return osx; 215 | } 216 | 217 | public String getWindows() 218 | { 219 | return windows; 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/install/tasks/http/JavaHttpClientImpl.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher.install.tasks.http; 2 | 3 | import com.google.common.hash.HashFunction; 4 | import com.google.common.hash.Hasher; 5 | import net.creeperhost.creeperlauncher.util.WebUtils; 6 | 7 | import java.io.IOException; 8 | import java.net.URI; 9 | import java.net.URL; 10 | import java.net.http.HttpClient; 11 | import java.net.http.HttpRequest; 12 | import java.net.http.HttpResponse; 13 | import java.nio.Buffer; 14 | import java.nio.ByteBuffer; 15 | import java.nio.file.Path; 16 | import java.util.List; 17 | import java.util.concurrent.CompletionStage; 18 | import java.util.concurrent.ExecutionException; 19 | import java.util.concurrent.Flow; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | public class JavaHttpClientImpl implements IHttpClient 23 | { 24 | @Override 25 | public String makeRequest(String url) 26 | { 27 | return null; 28 | } 29 | 30 | @Override 31 | public DownloadedFile doDownload(String url, Path destination, IProgressUpdater progressWatcher, HashFunction hashFunc, long maxSpeed) throws IOException, ExecutionException, InterruptedException 32 | { 33 | Hasher hasher = hashFunc.newHasher(); 34 | HttpClient client = HttpClient.newBuilder().executor(Runnable::run).build(); // always create 35 | HttpRequest request = HttpRequest.newBuilder() 36 | .uri(URI.create(url)) 37 | .build(); 38 | 39 | long fileSize = WebUtils.getFileSize(new URL(url)); 40 | 41 | PathBodyHandlerProgress pathBodyHandlerProgress = new PathBodyHandlerProgress(destination, progressWatcher, hasher, fileSize); 42 | 43 | Path send = client.sendAsync(request, pathBodyHandlerProgress).get().body(); // not really async - our client will run async things on same thread. bit of a hack, but async just froze. 44 | 45 | return new DownloadedFile(send, pathBodyHandlerProgress.wrapper.downloadedBytes.get(), hasher.hash()); 46 | } 47 | 48 | private void pushProgress(long totalRead, long delta, long contentLength, boolean done, IProgressUpdater progressWatcher) 49 | { 50 | if (progressWatcher != null) progressWatcher.update(totalRead, delta, contentLength, done); 51 | } 52 | 53 | class PathBodyHandlerProgress implements HttpResponse.BodyHandler 54 | { 55 | private final HttpResponse.BodyHandler pathBodyHandler; 56 | private BodySubscriberWrapper wrapper; 57 | private final Hasher hasher; 58 | private final IProgressUpdater progressWatcher; 59 | private final long fileSize; 60 | 61 | PathBodyHandlerProgress(Path destination, IProgressUpdater progressWatcher, Hasher hasher, long fileSize) 62 | { 63 | this.hasher = hasher; 64 | this.progressWatcher = progressWatcher; 65 | this.fileSize = fileSize; 66 | pathBodyHandler = HttpResponse.BodyHandlers.ofFile(destination); 67 | } 68 | 69 | @Override 70 | public HttpResponse.BodySubscriber apply(HttpResponse.ResponseInfo responseInfo) 71 | { 72 | return wrapper = new BodySubscriberWrapper(pathBodyHandler.apply(responseInfo), progressWatcher, hasher, fileSize); 73 | } 74 | } 75 | 76 | class BodySubscriberWrapper implements HttpResponse.BodySubscriber 77 | { 78 | 79 | private final HttpResponse.BodySubscriber delegate; 80 | private final IProgressUpdater progressWatcher; 81 | private final Hasher hasher; 82 | public AtomicInteger downloadedBytes = new AtomicInteger(); 83 | private long fileSize; 84 | 85 | BodySubscriberWrapper(HttpResponse.BodySubscriber delegate, IProgressUpdater progressWatcher, Hasher hasher, long fileSize) 86 | { 87 | this.fileSize = fileSize; 88 | this.hasher = hasher; 89 | this.delegate = delegate; 90 | this.progressWatcher = progressWatcher; 91 | } 92 | 93 | @Override 94 | public CompletionStage getBody() 95 | { 96 | return delegate.getBody(); 97 | } 98 | 99 | @Override 100 | public void onSubscribe(Flow.Subscription subscription) 101 | { 102 | delegate.onSubscribe(subscription); 103 | pushProgress(downloadedBytes.get(), 0, fileSize, false, progressWatcher); 104 | } 105 | 106 | @Override 107 | public void onNext(List item) 108 | { 109 | for (ByteBuffer bb : item) 110 | { 111 | hasher.putBytes(bb); 112 | bb.rewind(); 113 | } 114 | int sum = item.stream().mapToInt(Buffer::remaining).sum(); 115 | pushProgress(downloadedBytes.get(), sum, fileSize, false, progressWatcher); 116 | delegate.onNext(item); 117 | } 118 | 119 | @Override 120 | public void onError(Throwable throwable) 121 | { 122 | delegate.onError(throwable); 123 | } 124 | 125 | @Override 126 | public void onComplete() 127 | { 128 | delegate.onComplete(); 129 | pushProgress(downloadedBytes.get(), 0, fileSize, true, progressWatcher); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/main/java/net/creeperhost/creeperlauncher/Instances.java: -------------------------------------------------------------------------------- 1 | package net.creeperhost.creeperlauncher; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonObject; 5 | import net.creeperhost.minetogether.lib.cloudsaves.CloudSaveManager; 6 | import net.creeperhost.creeperlauncher.pack.LocalInstance; 7 | import net.creeperhost.creeperlauncher.util.ElapsedTimer; 8 | import net.creeperhost.creeperlauncher.util.FileUtils; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.util.*; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.Function; 18 | import java.util.stream.Collectors; 19 | 20 | public class Instances 21 | { 22 | private static final Logger LOGGER = LogManager.getLogger(); 23 | 24 | private static Map instances = new HashMap<>(); 25 | private static Map cloudInstances = new HashMap<>(); 26 | 27 | public static boolean addInstance(UUID uuid, LocalInstance instance) 28 | { 29 | Instances.instances.put(uuid, instance); 30 | return true; 31 | } 32 | 33 | public static LocalInstance getInstance(UUID uuid) 34 | { 35 | return Instances.instances.get(uuid); 36 | } 37 | 38 | public static List listInstances() 39 | { 40 | return instances.keySet().stream().map(UUID::toString).collect(Collectors.toList()); 41 | } 42 | 43 | //TODO, do these need to copy? 44 | public static List allInstances() 45 | { 46 | return new ArrayList<>(Instances.instances.values()); 47 | } 48 | 49 | public static List cloudInstances() 50 | { 51 | return new ArrayList<>(Instances.cloudInstances.values()); 52 | } 53 | 54 | public static void refreshInstances() { 55 | ElapsedTimer totalTimer = new ElapsedTimer(); 56 | Path instancesDir = Settings.getInstanceLocOr(Constants.INSTANCES_FOLDER_LOC); 57 | 58 | LOGGER.info("Reloading instances.."); 59 | instances.clear(); 60 | 61 | CompletableFuture cloudFuture = reloadCloudInstances(); 62 | 63 | if (!Files.exists(instancesDir)) { 64 | LOGGER.info("Instances directory missing, skipping.."); 65 | } else { 66 | ElapsedTimer timer = new ElapsedTimer(); 67 | List loadedInstances = FileUtils.listDir(instancesDir).stream() 68 | .parallel() 69 | .filter(e -> !e.getFileName().toString().startsWith(".")) 70 | .map(Instances::loadInstance) 71 | .filter(Objects::nonNull) 72 | .collect(Collectors.toList()); 73 | instances = loadedInstances.stream().collect(Collectors.toMap(LocalInstance::getUuid, Function.identity())); 74 | LOGGER.info("Loaded {} out of {} instances in {}.", instances.size(), loadedInstances.size(), timer.elapsedStr()); 75 | } 76 | 77 | if (cloudFuture != null) { 78 | cloudFuture.join(); 79 | } 80 | 81 | LOGGER.info("Finished instance reload in {}", totalTimer.elapsedStr()); 82 | 83 | } 84 | 85 | 86 | public static CompletableFuture reloadCloudInstances() { 87 | if (StringUtils.isNotEmpty(Constants.S3_HOST) && StringUtils.isNotEmpty(Constants.S3_BUCKET) && StringUtils.isNotEmpty(Constants.S3_KEY) && StringUtils.isNotEmpty(Constants.S3_SECRET)) { 88 | return CompletableFuture.runAsync(() -> { 89 | ElapsedTimer timer = new ElapsedTimer(); 90 | LOGGER.info("Loading cloud instances"); 91 | cloudInstances = loadCloudInstances(); 92 | LOGGER.info("Loaded {} cloud instances in {}.", cloudInstances.size(), timer.elapsedStr()); 93 | }); 94 | } 95 | LOGGER.info("Skipping Cloud instance reload."); 96 | return null; 97 | } 98 | 99 | private static LocalInstance loadInstance(Path path) { 100 | Path json = path.resolve("instance.json"); 101 | if (Files.notExists(json)) { 102 | LOGGER.error("Instance missing 'instance.json', Ignoring. {}", json.toAbsolutePath()); 103 | return null; 104 | } 105 | try { 106 | LocalInstance localInstance = new LocalInstance(path); 107 | if (!localInstance.installComplete) { 108 | LOGGER.error("Instance install never completed, Ignoring. {}", json.toAbsolutePath()); 109 | return null; 110 | } 111 | return localInstance; 112 | } catch(Exception e) { 113 | LOGGER.error("Instance has corrupted 'instance.json'. {}", json.toAbsolutePath()); 114 | LOGGER.error(e); 115 | return null; 116 | } 117 | } 118 | 119 | private static HashMap loadCloudInstances() 120 | { 121 | List uuidList = CloudSaveManager.getPrefixes(); 122 | HashMap hashMap = new HashMap<>(); 123 | 124 | for (UUID uuid : uuidList) 125 | { 126 | try 127 | { 128 | if(Instances.getInstance(uuid) == null) 129 | { 130 | String jsonResp = CloudSaveManager.getFile(uuid.toString() + "/instance.json"); 131 | Gson gson = new Gson(); 132 | JsonObject object = gson.fromJson(jsonResp, JsonObject.class); 133 | hashMap.put(uuid, object); 134 | } 135 | } catch (Exception e) 136 | { 137 | LOGGER.error("Invalid cloudsave found with UUID of {}", uuid); 138 | } 139 | 140 | } 141 | return hashMap; 142 | } 143 | } 144 | --------------------------------------------------------------------------------