Turn to java

This commit is contained in:
huangyuhui
2018-01-01 23:12:59 +08:00
parent d8be9abcc5
commit a40c5fdd40
307 changed files with 17342 additions and 10414 deletions

View File

@@ -0,0 +1,40 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
import java.net.Proxy;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public abstract class Account {
public abstract String getUsername();
public AuthInfo logIn() throws AuthenticationException {
return logIn(Proxy.NO_PROXY);
}
public abstract AuthInfo logIn(Proxy proxy) throws AuthenticationException;
public abstract void logOut();
public abstract Map<Object, Object> toStorage();
}

View File

@@ -15,17 +15,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.task
package org.jackhuang.hmcl.auth;
import java.util.Map;
/**
* The tasks that provides a way to execute tasks parallelly.
* Fails when some of [tasks] failed.
*
* @param tasks the tasks that can be executed parallelly.
* @author huangyuhui
*/
class ParallelTask(vararg tasks: Task): Task() {
override val hidden: Boolean = true
override val dependents: Collection<Task> = listOf(*tasks)
public abstract class AccountFactory<T extends Account> {
override fun execute() {}
}
public final T fromUsername(String username) {
return fromUsername(username, "");
}
public abstract T fromUsername(String username, String password);
public abstract T fromStorage(Map<Object, Object> storage);
}

View File

@@ -0,0 +1,96 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
/**
*
* @author huangyuhui
*/
public final class AuthInfo {
private final String username;
private final String userId;
private final String authToken;
private final UserType userType;
private final String userProperties;
private final String userPropertyMap;
public AuthInfo(String username, String userId, String authToken) {
this(username, userId, authToken, UserType.LEGACY);
}
public AuthInfo(String username, String userId, String authToken, UserType userType) {
this(username, userId, authToken, userType, "{}");
}
public AuthInfo(String username, String userId, String authToken, UserType userType, String userProperties) {
this(username, userId, authToken, userType, userProperties, "{}");
}
public AuthInfo(String username, String userId, String authToken, UserType userType, String userProperties, String userPropertyMap) {
this.username = username;
this.userId = userId;
this.authToken = authToken;
this.userType = userType;
this.userProperties = userProperties;
this.userPropertyMap = userPropertyMap;
}
public AuthInfo(GameProfile profile, String authToken, UserType userType, String userProperties) {
this(profile.getName(), UUIDTypeAdapter.fromUUID(profile.getId()), authToken, userType, userProperties);
}
public String getUsername() {
return username;
}
public String getUserId() {
return userId;
}
public String getAuthToken() {
return authToken;
}
public UserType getUserType() {
return userType;
}
/**
* Properties of this user.
* Don't know the difference between user properties and user property map.
*
* @return the user property map in JSON.
*/
public String getUserProperties() {
return userProperties;
}
/**
* Properties of this user.
* Don't know the difference between user properties and user property map.
*
* @return the user property map in JSON.
*/
public String getUserPropertyMap() {
return userPropertyMap;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
/**
*
* @author huangyuhui
*/
public class AuthenticationException extends Exception {
public AuthenticationException() {
super();
}
public AuthenticationException(String message) {
super(message);
}
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
import java.net.Proxy;
import java.util.Map;
import java.util.Objects;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.StringUtils;
/**
*
* @author huang
*/
public class OfflineAccount extends Account {
private final String username;
private final String uuid;
OfflineAccount(String username, String uuid) {
Objects.requireNonNull(username);
Objects.requireNonNull(uuid);
this.username = username;
this.uuid = uuid;
if (StringUtils.isBlank(username))
throw new IllegalArgumentException("Username cannot be blank");
}
public String getUuid() {
return uuid;
}
@Override
public String getUsername() {
return username;
}
@Override
public AuthInfo logIn(Proxy proxy) throws AuthenticationException {
if (StringUtils.isBlank(username) || StringUtils.isBlank(uuid))
throw new AuthenticationException("Username cannot be empty");
return new AuthInfo(username, uuid, uuid);
}
@Override
public void logOut() {
// Offline account need not log out.
}
@Override
public Map<Object, Object> toStorage() {
return Lang.mapOf(
new Pair<>("uuid", uuid),
new Pair<>("username", username)
);
}
@Override
public String toString() {
return "OfflineAccount[username=" + username + ", uuid=" + uuid + "]";
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
import java.util.Map;
import org.jackhuang.hmcl.util.DigestUtils;
/**
*
* @author huangyuhui
*/
public class OfflineAccountFactory extends AccountFactory<OfflineAccount> {
public static final OfflineAccountFactory INSTANCE = new OfflineAccountFactory();
private OfflineAccountFactory() {
}
@Override
public OfflineAccount fromUsername(String username, String password) {
return new OfflineAccount(username, getUUIDFromUserName(username));
}
@Override
public OfflineAccount fromStorage(Map<Object, Object> storage) {
Object username = storage.get("username");
if (username == null || !(username instanceof String))
throw new IllegalStateException("Offline account configuration malformed.");
Object uuid = storage.get("uuid");
if (uuid == null || !(uuid instanceof String))
uuid = getUUIDFromUserName((String) username);
return new OfflineAccount((String) username, (String) uuid);
}
private static String getUUIDFromUserName(String username) {
return DigestUtils.md5Hex(username);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public enum UserType {
LEGACY,
MOJANG;
public static UserType fromName(String name) {
return BY_NAME.get(name.toLowerCase());
}
public static UserType fromLegacy(boolean isLegacy) {
return isLegacy ? LEGACY : MOJANG;
}
static {
HashMap<String, UserType> byName = new HashMap<>();
for (UserType type : values())
byName.put(type.name().toLowerCase(), type);
BY_NAME = Collections.unmodifiableMap(byName);
}
public static final Map<String, UserType> BY_NAME;
}

View File

@@ -0,0 +1,61 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import java.util.Map;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair;
/**
*
* @author huangyuhui
*/
public final class AuthenticationRequest {
/**
* The user name of Minecraft account.
*/
private final String username;
/**
* The password of Minecraft account.
*/
private final String password;
/**
* The client token of this game.
*/
private final String clientToken;
private final Map<String, Object> agent = Lang.mapOf(
new Pair("name", "minecraft"),
new Pair("version", 1));
private final boolean requestUser = true;
public AuthenticationRequest(String username, String password, String clientToken) {
this.username = username;
this.password = password;
this.clientToken = clientToken;
}
public String getUsername() {
return username;
}
public String getClientToken() {
return clientToken;
}
public Map<String, Object> getAgent() {
return agent;
}
public boolean isRequestUser() {
return requestUser;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.UUID;
/**
*
* @author huang
*/
public final class GameProfile {
private final UUID id;
private final String name;
private final PropertyMap properties;
private final boolean legacy;
public GameProfile() {
this(null, null);
}
public GameProfile(UUID id, String name) {
this(id, name, new PropertyMap(), false);
}
public GameProfile(UUID id, String name, PropertyMap properties, boolean legacy) {
this.id = id;
this.name = name;
this.properties = properties;
this.legacy = legacy;
}
public UUID getId() {
return id;
}
public String getName() {
return name;
}
public PropertyMap getProperties() {
return properties;
}
public boolean isLegacy() {
return legacy;
}
public static class Serializer implements JsonSerializer<GameProfile>, JsonDeserializer<GameProfile> {
public static final Serializer INSTANCE = new Serializer();
private Serializer() {
}
@Override
public JsonElement serialize(GameProfile src, Type type, JsonSerializationContext context) {
JsonObject result = new JsonObject();
if (src.getId() != null)
result.add("id", context.serialize(src.getId()));
if (src.getName() != null)
result.addProperty("name", src.getName());
return result;
}
@Override
public GameProfile deserialize(JsonElement je, Type type, JsonDeserializationContext context) throws JsonParseException {
if (!(je instanceof JsonObject))
throw new JsonParseException("The json element is not a JsonObject.");
JsonObject json = (JsonObject) je;
UUID id = json.has("id") ? context.deserialize(json.get("id"), UUID.class) : null;
String name = json.has("name") ? json.getAsJsonPrimitive("name").getAsString() : null;
return new GameProfile(id, name);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import org.jackhuang.hmcl.auth.AuthenticationException;
/**
*
* @author huangyuhui
*/
public final class InvalidCredentialsException extends AuthenticationException {
private final YggdrasilAccount account;
public InvalidCredentialsException(YggdrasilAccount account) {
this.account = account;
}
public YggdrasilAccount getAccount() {
return account;
}
}

View File

@@ -15,20 +15,24 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth
package org.jackhuang.hmcl.auth.yggdrasil;
enum class UserType() {
LEGACY,
MOJANG;
import org.jackhuang.hmcl.auth.AuthenticationException;
companion object {
/**
*
* @author huangyuhui
*/
public class InvalidTokenException extends AuthenticationException {
fun fromName(name: String) = BY_NAME[name.toLowerCase()]
fun fromLegacy(isLegacy: Boolean) = if (isLegacy) LEGACY else MOJANG
private YggdrasilAccount account;
private val BY_NAME = HashMap<String, UserType>().apply {
for (type in values())
this[type.name.toLowerCase()] = type
}
public InvalidTokenException(YggdrasilAccount account) {
super();
this.account = account;
}
}
public YggdrasilAccount getAccount() {
return account;
}
}

View File

@@ -1,7 +1,7 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -15,12 +15,23 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.task
package org.jackhuang.hmcl.auth.yggdrasil;
import org.jackhuang.hmcl.util.AutoTypingMap
public class Property {
internal class SimpleTask @JvmOverloads constructor(private val runnable: (AutoTypingMap<String>) -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
override fun execute() {
runnable(variables!!)
private final String name;
private final String value;
public Property(String name, String value) {
this.name = name;
this.value = value;
}
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.jackhuang.hmcl.util.Lang;
public final class PropertyMap extends HashMap<String, Property> {
public List<Map<String, String>> toList() {
List<Map<String, String>> properties = new ArrayList<>();
for (Property profileProperty : values()) {
Map<String, String> property = new HashMap<>();
property.put("name", profileProperty.getName());
property.put("value", profileProperty.getValue());
properties.add(property);
}
return properties;
}
public void fromList(List<?> list) {
for (Object propertyMap : list) {
if (!(propertyMap instanceof Map<?, ?>))
continue;
Optional<String> name = Lang.get((Map<?, ?>) propertyMap, "name", String.class);
Optional<String> value = Lang.get((Map<?, ?>) propertyMap, "value", String.class);
if (name.isPresent() && value.isPresent())
put(name.get(), new Property(name.get(), value.get()));
}
}
public static class Serializer implements JsonSerializer<PropertyMap>, JsonDeserializer<PropertyMap> {
public static final Serializer INSTANCE = new Serializer();
private Serializer() {
}
@Override
public PropertyMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
PropertyMap result = new PropertyMap();
if (json instanceof JsonObject) {
for (Map.Entry<String, JsonElement> entry : ((JsonObject) json).entrySet())
if (entry.getValue() instanceof JsonArray)
for (JsonElement element : (JsonArray) entry.getValue())
result.put(entry.getKey(), new Property(entry.getKey(), element.getAsString()));
} else if ((json instanceof JsonArray))
for (JsonElement element : (JsonArray) json)
if ((element instanceof JsonObject)) {
JsonObject object = (JsonObject) element;
String name = object.getAsJsonPrimitive("name").getAsString();
String value = object.getAsJsonPrimitive("value").getAsString();
result.put(name, new Property(name, value));
}
return result;
}
@Override
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
JsonArray result = new JsonArray();
for (Property property : src.values()) {
JsonObject object = new JsonObject();
object.addProperty("name", property.getName());
object.addProperty("value", property.getValue());
result.add(object);
}
return result;
}
}
public static class LegacySerializer
implements JsonSerializer<PropertyMap> {
@Override
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
for (PropertyMap.Entry<String, Property> entry : src.entrySet()) {
JsonArray values = new JsonArray();
values.add(new JsonPrimitive(entry.getValue().getValue()));
result.add(entry.getKey(), values);
}
return result;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
/**
*
* @author huang
*/
public final class RefreshRequest {
private final String accessToken;
private final String clientToken;
private final GameProfile selectedProfile;
private final boolean requestUser;
public RefreshRequest(String accessToken, String clientToken) {
this(accessToken, clientToken, null);
}
public RefreshRequest(String accessToken, String clientToken, GameProfile selectedProfile) {
this(accessToken, clientToken, selectedProfile, true);
}
public RefreshRequest(String accessToken, String clientToken, GameProfile selectedProfile, boolean requestUser) {
this.accessToken = accessToken;
this.clientToken = clientToken;
this.selectedProfile = selectedProfile;
this.requestUser = requestUser;
}
public String getAccessToken() {
return accessToken;
}
public String getClientToken() {
return clientToken;
}
public GameProfile getSelectedProfile() {
return selectedProfile;
}
public boolean isRequestUser() {
return requestUser;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
/**
*
* @author huangyuhui
*/
public final class Response {
private final String accessToken;
private final String clientToken;
private final GameProfile selectedProfile;
private final GameProfile[] availableProfiles;
private final User user;
private final String error;
private final String errorMessage;
private final String cause;
public Response() {
this(null, null, null, null, null, null, null, null);
}
public Response(String accessToken, String clientToken, GameProfile selectedProfile, GameProfile[] availableProfiles, User user, String error, String errorMessage, String cause) {
this.accessToken = accessToken;
this.clientToken = clientToken;
this.selectedProfile = selectedProfile;
this.availableProfiles = availableProfiles;
this.user = user;
this.error = error;
this.errorMessage = errorMessage;
this.cause = cause;
}
public String getAccessToken() {
return accessToken;
}
public String getClientToken() {
return clientToken;
}
public GameProfile getSelectedProfile() {
return selectedProfile;
}
public GameProfile[] getAvailableProfiles() {
return availableProfiles;
}
public User getUser() {
return user;
}
public String getError() {
return error;
}
public String getErrorMessage() {
return errorMessage;
}
public String getCause() {
return cause;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huang
*/
public final class User implements Validation {
private final String id;
private final PropertyMap properties;
public User(String id) {
this(id, null);
}
public User(String id, PropertyMap properties) {
this.id = id;
this.properties = properties;
}
public String getId() {
return id;
}
public PropertyMap getProperties() {
return properties;
}
@Override
public void validate() throws JsonParseException {
if (StringUtils.isBlank(id))
throw new JsonParseException("User id cannot be empty.");
}
}

View File

@@ -15,19 +15,28 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.task
package org.jackhuang.hmcl.auth.yggdrasil;
import org.jackhuang.hmcl.util.AutoTypingMap
import java.util.concurrent.Callable
/**
*
* @author huangyuhui
*/
public final class ValidateRequest {
internal class TaskCallable<V>(override val id: String, private val callable: Callable<V>) : TaskResult<V>() {
override fun execute() {
result = callable.call()
private final String accessToken;
private final String clientToken;
public ValidateRequest(String accessToken, String clientToken) {
this.accessToken = accessToken;
this.clientToken = clientToken;
}
public String getAccessToken() {
return accessToken;
}
public String getClientToken() {
return clientToken;
}
}
internal class TaskCallable2<V>(override val id: String, private val callable: (AutoTypingMap<String>) -> V) : TaskResult<V>() {
override fun execute() {
result = callable(variables!!)
}
}

View File

@@ -0,0 +1,274 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.UserType;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
/**
*
* @author huang
*/
public final class YggdrasilAccount extends Account {
private final String username;
private String password;
private String userId;
private String accessToken = null;
private String clientToken = randomToken();
private boolean isOnline = false;
private PropertyMap userProperties = new PropertyMap();
private GameProfile selectedProfile = null;
private GameProfile[] profiles;
private UserType userType = UserType.LEGACY;
public YggdrasilAccount(String username) {
this.username = username;
}
@Override
public String getUsername() {
return username;
}
void setPassword(String password) {
this.password = password;
}
public String getUserId() {
return userId;
}
void setUserId(String userId) {
this.userId = userId;
}
void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
String getClientToken() {
return clientToken;
}
void setClientToken(String clientToken) {
this.clientToken = clientToken;
}
PropertyMap getUserProperties() {
return userProperties;
}
public GameProfile getSelectedProfile() {
return selectedProfile;
}
void setSelectedProfile(GameProfile selectedProfile) {
this.selectedProfile = selectedProfile;
}
public boolean isLoggedIn() {
return StringUtils.isNotBlank(accessToken);
}
public boolean canPlayOnline() {
return isLoggedIn() && selectedProfile != null && isOnline;
}
public boolean canLogIn() {
return !canPlayOnline() && StringUtils.isNotBlank(username)
&& (StringUtils.isNotBlank(password) || StringUtils.isNotBlank(accessToken));
}
@Override
public AuthInfo logIn(Proxy proxy) throws AuthenticationException {
if (canPlayOnline())
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
else {
logIn0(proxy);
if (!isLoggedIn())
throw new AuthenticationException("Wrong password for account " + username);
if (selectedProfile == null)
// TODO: multi-available-profiles support
throw new UnsupportedOperationException("Do not support multi-available-profiles account yet.");
else
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
}
}
private void logIn0(Proxy proxy) throws AuthenticationException {
if (StringUtils.isNotBlank(accessToken)) {
if (StringUtils.isBlank(userId))
if (StringUtils.isNotBlank(username))
userId = username;
else
throw new AuthenticationException("Invalid uuid and username");
if (checkTokenValidity(proxy)) {
isOnline = true;
return;
}
logIn1(ROUTE_REFRESH, new RefreshRequest(accessToken, clientToken), proxy);
} else if (StringUtils.isNotBlank(password))
logIn1(ROUTE_AUTHENTICATE, new AuthenticationRequest(username, password, clientToken), proxy);
else
throw new AuthenticationException("Password cannot be blank");
}
private void logIn1(URL url, Object input, Proxy proxy) throws AuthenticationException {
Response response = makeRequest(url, input, proxy);
if (response == null || !clientToken.equals(response.getClientToken()))
throw new AuthenticationException("Client token changed");
if (response.getSelectedProfile() != null)
userType = UserType.fromLegacy(response.getSelectedProfile().isLegacy());
else if (response.getAvailableProfiles() != null && response.getAvailableProfiles().length > 0)
userType = UserType.fromLegacy(response.getAvailableProfiles()[0].isLegacy());
User user = response.getUser();
if (user == null || user.getId() == null)
userId = null;
else
userId = user.getId();
isOnline = true;
profiles = response.getAvailableProfiles();
selectedProfile = response.getSelectedProfile();
userProperties.clear();
accessToken = response.getAccessToken();
if (user != null && user.getProperties() != null)
userProperties.putAll(user.getProperties());
}
@Override
public void logOut() {
password = null;
userId = null;
accessToken = null;
isOnline = false;
userProperties.clear();
profiles = null;
selectedProfile = null;
}
@Override
public Map<Object, Object> toStorage() {
HashMap<Object, Object> result = new HashMap<>();
result.put(STORAGE_KEY_USER_NAME, getUsername());
result.put(STORAGE_KEY_CLIENT_TOKEN, getClientToken());
if (getUserId() != null)
result.put(STORAGE_KEY_USER_ID, getUserId());
if (!userProperties.isEmpty())
result.put(STORAGE_KEY_USER_PROPERTIES, userProperties.toList());
GameProfile profile = selectedProfile;
if (profile != null && profile.getName() != null && profile.getId() != null) {
result.put(STORAGE_KEY_PROFILE_NAME, profile.getName());
result.put(STORAGE_KEY_PROFILE_ID, profile.getId());
if (!profile.getProperties().isEmpty())
result.put(STORAGE_KEY_PROFILE_PROPERTIES, profile.getProperties().toList());
}
if (StringUtils.isNotBlank(accessToken))
result.put(STORAGE_KEY_ACCESS_TOKEN, accessToken);
return result;
}
private Response makeRequest(URL url, Object input, Proxy proxy) throws AuthenticationException {
try {
String jsonResult = input == null ? NetworkUtils.doGet(url, proxy) : NetworkUtils.doPost(url, GSON.toJson(input), "application/json", proxy);
Response response = GSON.fromJson(jsonResult, Response.class);
if (response == null)
return null;
if (!StringUtils.isBlank(response.getError())) {
if (response.getErrorMessage() != null)
if (response.getErrorMessage().contains("Invalid credentials"))
throw new InvalidCredentialsException(this);
else if (response.getErrorMessage().contains("Invalid token"))
throw new InvalidTokenException(this);
throw new AuthenticationException(response.getError() + ": " + response.getErrorMessage());
}
return response;
} catch (IOException e) {
throw new AuthenticationException("Unable to connect to authentication server", e);
} catch (JsonParseException e) {
throw new AuthenticationException("Unable to parse server response", e);
}
}
private boolean checkTokenValidity(Proxy proxy) {
if (accessToken == null)
return false;
try {
makeRequest(ROUTE_VALIDATE, new ValidateRequest(accessToken, clientToken), proxy);
return true;
} catch (AuthenticationException e) {
return false;
}
}
@Override
public String toString() {
return "YggdrasilAccount[username=" + getUsername() + "]";
}
private static final String BASE_URL = "https://authserver.mojang.com/";
private static final URL ROUTE_AUTHENTICATE = NetworkUtils.toURL(BASE_URL + "authenticate");
private static final URL ROUTE_REFRESH = NetworkUtils.toURL(BASE_URL + "refresh");
private static final URL ROUTE_VALIDATE = NetworkUtils.toURL(BASE_URL + "validate");
static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken";
static final String STORAGE_KEY_PROFILE_NAME = "displayName";
static final String STORAGE_KEY_PROFILE_ID = "uuid";
static final String STORAGE_KEY_PROFILE_PROPERTIES = "profileProperties";
static final String STORAGE_KEY_USER_NAME = "username";
static final String STORAGE_KEY_USER_ID = "userid";
static final String STORAGE_KEY_USER_PROPERTIES = "userProperties";
static final String STORAGE_KEY_CLIENT_TOKEN = "clientToken";
public static String randomToken() {
return UUIDTypeAdapter.fromUUID(UUID.randomUUID());
}
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(GameProfile.class, GameProfile.Serializer.INSTANCE)
.registerTypeAdapter(PropertyMap.class, PropertyMap.Serializer.INSTANCE)
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
.create();
}

View File

@@ -0,0 +1,70 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.util.Lang;
import static org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount.*;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
/**
*
* @author huangyuhui
*/
public final class YggdrasilAccountFactory extends AccountFactory<YggdrasilAccount> {
public static final YggdrasilAccountFactory INSTANCE = new YggdrasilAccountFactory();
private YggdrasilAccountFactory() {
}
@Override
public YggdrasilAccount fromUsername(String username, String password) {
YggdrasilAccount account = new YggdrasilAccount(username);
account.setPassword(password);
return account;
}
@Override
public YggdrasilAccount fromStorage(Map<Object, Object> storage) {
String username = Lang.get(storage, STORAGE_KEY_USER_NAME, String.class)
.orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_USER_NAME));
YggdrasilAccount account = new YggdrasilAccount(username);
account.setUserId(Lang.get(storage, STORAGE_KEY_USER_ID, String.class, username));
account.setAccessToken(Lang.get(storage, STORAGE_KEY_ACCESS_TOKEN, String.class, null));
account.setClientToken(Lang.get(storage, STORAGE_KEY_CLIENT_TOKEN, String.class)
.orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_CLIENT_TOKEN)));
Lang.get(storage, STORAGE_KEY_USER_PROPERTIES, List.class)
.ifPresent(account.getUserProperties()::fromList);
Optional<String> profileId = Lang.get(storage, STORAGE_KEY_PROFILE_ID, String.class);
Optional<String> profileName = Lang.get(storage, STORAGE_KEY_PROFILE_NAME, String.class);
GameProfile profile = null;
if (profileId.isPresent() && profileName.isPresent()) {
profile = new GameProfile(UUIDTypeAdapter.fromString(profileId.get()), profileName.get());
Lang.get(storage, STORAGE_KEY_PROFILE_PROPERTIES, List.class)
.ifPresent(profile.getProperties()::fromList);
}
account.setSelectedProfile(profile);
return account;
}
}

View File

@@ -15,14 +15,18 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download
package org.jackhuang.hmcl.download;
abstract class AbstractDependencyManager
: DependencyManager {
abstract val downloadProvider: DownloadProvider
/**
*
* @author huangyuhui
*/
public abstract class AbstractDependencyManager implements DependencyManager {
override fun getVersionList(id: String): VersionList<*> {
return downloadProvider.getVersionListById(id)
public abstract DownloadProvider getDownloadProvider();
@Override
public VersionList<?> getVersionList(String id) {
return getDownloadProvider().getVersionListById(id);
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.forge.ForgeVersionList;
import org.jackhuang.hmcl.download.game.GameVersionList;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
/**
*
* @author huang
*/
public class BMCLAPIDownloadProvider implements DownloadProvider {
public static final BMCLAPIDownloadProvider INSTANCE = new BMCLAPIDownloadProvider();
private BMCLAPIDownloadProvider() {
}
@Override
public String getLibraryBaseURL() {
return "http://bmclapi2.bangbang93.com/libraries/";
}
@Override
public String getVersionListURL() {
return "http://bmclapi2.bangbang93.com/mc/game/version_manifest.json";
}
@Override
public String getVersionBaseURL() {
return "http://bmclapi2.bangbang93.com/versions/";
}
@Override
public String getAssetIndexBaseURL() {
return "http://bmclapi2.bangbang93.com/indexes/";
}
@Override
public String getAssetBaseURL() {
return "http://bmclapi2.bangbang93.com/assets/";
}
@Override
public VersionList<?> getVersionListById(String id) {
switch (id) {
case "game":
return GameVersionList.INSTANCE;
case "forge":
return ForgeVersionList.INSTANCE;
case "liteloader":
return LiteLoaderVersionList.INSTANCE;
case "optifine":
return OptiFineBMCLVersionList.INSTANCE;
default:
throw new IllegalArgumentException("Unrecognized version list id: " + id);
}
}
@Override
public String injectURL(String baseURL) {
return baseURL
.replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
.replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com")
.replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
.replace("http://files.minecraftforge.net/maven", "https://bmclapi2.bangbang93.com/maven")
.replace("http://dl.liteloader.com/versions/versions.json", "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
.replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven");
}
}

View File

@@ -0,0 +1,100 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download;
import java.net.Proxy;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.download.game.GameLoggingDownloadTask;
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
/**
* Note: This class has no state.
*
* @author huangyuhui
*/
public class DefaultDependencyManager extends AbstractDependencyManager {
private final DefaultGameRepository repository;
private final DownloadProvider downloadProvider;
private final Proxy proxy;
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider) {
this(repository, downloadProvider, Proxy.NO_PROXY);
}
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, Proxy proxy) {
this.repository = repository;
this.downloadProvider = downloadProvider;
this.proxy = proxy;
}
@Override
public DefaultGameRepository getGameRepository() {
return repository;
}
@Override
public DownloadProvider getDownloadProvider() {
return downloadProvider;
}
@Override
public Proxy getProxy() {
return proxy;
}
@Override
public GameBuilder gameBuilder() {
return new DefaultGameBuilder(this);
}
@Override
public Task checkGameCompletionAsync(Version version) {
return new ParallelTask(
new GameAssetDownloadTask(this, version),
new GameLoggingDownloadTask(this, version),
new GameLibrariesTask(this, version)
);
}
@Override
public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
switch (libraryId) {
case "forge":
return new ForgeInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
case "liteloader":
return new LiteLoaderInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
case "optifine":
return new OptiFineInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
default:
throw new IllegalArgumentException("Library id " + libraryId + " is unrecognized.");
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download;
import java.util.function.Function;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameDownloadTask;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.download.game.GameLoggingDownloadTask;
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.Constants;
/**
*
* @author huangyuhui
*/
public class DefaultGameBuilder extends GameBuilder {
private final DefaultDependencyManager dependencyManager;
private final DownloadProvider downloadProvider;
public DefaultGameBuilder(DefaultDependencyManager dependencyManager) {
this.dependencyManager = dependencyManager;
this.downloadProvider = dependencyManager.getDownloadProvider();
}
@Override
public Task buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
Version version = Constants.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class);
variables.set("version", version);
version = version.setId(name).setJar(null);
Task result = new ParallelTask(
new GameAssetDownloadTask(dependencyManager, version),
new GameLoggingDownloadTask(dependencyManager, version),
new GameDownloadTask(dependencyManager, version),
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant.
if (toolVersions.containsKey("forge"))
result = result.then(libraryTaskHelper(gameVersion, "forge"));
if (toolVersions.containsKey("liteloader"))
result = result.then(libraryTaskHelper(gameVersion, "liteloader"));
if (toolVersions.containsKey("optifine"))
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
return result;
});
}
private Function<AutoTypingMap<String>, Task> libraryTaskHelper(String gameVersion, String libraryId) {
return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId));
}
}

View File

@@ -15,27 +15,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.GameRepository
import org.jackhuang.hmcl.game.Version
import org.jackhuang.hmcl.task.Task
import java.net.Proxy
import java.net.Proxy;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
/**
* Do everything that will connect to Internet.
* Downloading Minecraft files.
*
* @author huangyuhui
*/
interface DependencyManager {
public interface DependencyManager {
/**
* The relied game repository.
*/
val repository: GameRepository
GameRepository getGameRepository();
/**
* The proxy that all network operations should go through.
*/
val proxy: Proxy
Proxy getProxy();
/**
* Check if the game is complete.
@@ -43,12 +46,12 @@ interface DependencyManager {
*
* @return the task to check game completion.
*/
fun checkGameCompletionAsync(version: Version): Task
Task checkGameCompletionAsync(Version version);
/**
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
*/
fun gameBuilder(): GameBuilder
GameBuilder gameBuilder();
/**
* Install a library to a version.
@@ -60,7 +63,7 @@ interface DependencyManager {
* @param libraryVersion the version of being installed library.
* @return the task to install the specific library.
*/
fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
/**
* Get registered version list.
@@ -68,5 +71,5 @@ interface DependencyManager {
* @param id the id of version list. i.e. game, forge, liteloader, optifine
* @throws IllegalArgumentException if the version list of specific id is not found.
*/
fun getVersionList(id: String): VersionList<*>
}
VersionList<?> getVersionList(String id);
}

View File

@@ -15,17 +15,24 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download
package org.jackhuang.hmcl.download;
/**
* The service provider that provides Minecraft online file downloads.
*
* @author huangyuhui
*/
interface DownloadProvider {
val libraryBaseURL: String
val versionListURL: String
val versionBaseURL: String
val assetIndexBaseURL: String
val assetBaseURL: String
public interface DownloadProvider {
String getLibraryBaseURL();
String getVersionListURL();
String getVersionBaseURL();
String getAssetIndexBaseURL();
String getAssetBaseURL();
/**
* Inject into original URL provided by Mojang and Forge.
@@ -36,12 +43,13 @@ interface DownloadProvider {
* @param baseURL original URL provided by Mojang and Forge.
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
*/
fun injectURL(baseURL: String): String
String injectURL(String baseURL);
/**
* the specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
*
* @param id the id of specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
* @return the version list
*/
fun getVersionListById(id: String): VersionList<*>
}
VersionList<?> getVersionListById(String id);
}

View File

@@ -15,45 +15,57 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.task.Task
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.jackhuang.hmcl.task.Task;
/**
* The builder which provide a task to build Minecraft environment.
*
* @author huangyuhui
*/
abstract class GameBuilder {
var name: String = ""
protected var gameVersion: String = ""
protected var toolVersions = HashMap<String, String>()
public abstract class GameBuilder {
protected String name = "";
protected String gameVersion = "";
protected Map<String, String> toolVersions = new HashMap<>();
public String getName() {
return name;
}
/**
* The new game version name, for .minecraft/<version name>.
*
* @param name the name of new game version.
*/
fun name(name: String): GameBuilder {
this.name = name
return this
public GameBuilder name(String name) {
this.name = Objects.requireNonNull(name);
return this;
}
fun gameVersion(version: String): GameBuilder {
gameVersion = version
return this
public GameBuilder gameVersion(String version) {
this.gameVersion = Objects.requireNonNull(version);
return this;
}
/**
* @param id the core library id. i.e. "forge", "liteloader", "optifine"
* @param version the version of the core library. For documents, you can first try [VersionList.versions]
*/
fun version(id: String, version: String): GameBuilder {
toolVersions[id] = version
return this
public GameBuilder version(String id, String version) {
if ("game".equals(id))
gameVersion(version);
else
toolVersions.put(id, version);
return this;
}
/**
* @return the task that can build thw whole Minecraft environment
*/
abstract fun buildAsync(): Task
}
public abstract Task buildAsync();
}

View File

@@ -0,0 +1,84 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.forge.ForgeVersionList;
import org.jackhuang.hmcl.download.game.GameVersionList;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
import org.jackhuang.hmcl.download.optifine.OptiFineVersionList;
/**
* @see {@link http://wiki.vg}
* @author huangyuhui
*/
public final class MojangDownloadProvider implements DownloadProvider {
public static final MojangDownloadProvider INSTANCE = new MojangDownloadProvider();
private MojangDownloadProvider() {
}
@Override
public String getLibraryBaseURL() {
return "https://libraries.minecraft.net/";
}
@Override
public String getVersionListURL() {
return "https://launchermeta.mojang.com/mc/game/version_manifest.json";
}
@Override
public String getVersionBaseURL() {
return "http://s3.amazonaws.com/Minecraft.Download/versions/";
}
@Override
public String getAssetIndexBaseURL() {
return "http://s3.amazonaws.com/Minecraft.Download/indexes/";
}
@Override
public String getAssetBaseURL() {
return "http://resources.download.minecraft.net/";
}
@Override
public VersionList<?> getVersionListById(String id) {
switch (id) {
case "game":
return GameVersionList.INSTANCE;
case "forge":
return ForgeVersionList.INSTANCE;
case "liteloader":
return LiteLoaderVersionList.INSTANCE;
case "optifine":
return OptiFineVersionList.INSTANCE;
default:
throw new IllegalArgumentException("Unrecognized version list id: " + id);
}
}
@Override
public String injectURL(String baseURL) {
if (baseURL.contains("net/minecraftforge/forge"))
return baseURL;
else
return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven");
}
}

View File

@@ -0,0 +1,96 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download;
import java.util.Comparator;
import java.util.Objects;
import org.jackhuang.hmcl.util.VersionNumber;
/**
* The remote version.
*
* @author huangyuhui
*/
public final class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
private final String gameVersion;
private final String selfVersion;
private final String url;
private final T tag;
/**
* Constructor.
*
* @param gameVersion the Minecraft version that this remote version suits.
* @param selfVersion the version string of the remote version.
* @param url the installer or universal jar URL.
* @param tag some necessary information for Installer Task.
*/
public RemoteVersion(String gameVersion, String selfVersion, String url, T tag) {
this.gameVersion = Objects.requireNonNull(gameVersion);
this.selfVersion = Objects.requireNonNull(selfVersion);
this.url = Objects.requireNonNull(url);
this.tag = tag;
}
public String getGameVersion() {
return gameVersion;
}
public String getSelfVersion() {
return selfVersion;
}
public T getTag() {
return tag;
}
public String getUrl() {
return url;
}
@Override
public boolean equals(Object obj) {
return obj instanceof RemoteVersion && Objects.equals(selfVersion, ((RemoteVersion) obj).selfVersion);
}
@Override
public int hashCode() {
return selfVersion.hashCode();
}
@Override
public int compareTo(RemoteVersion<T> o) {
// newer versions are smaller than older versions
return -selfVersion.compareTo(o.selfVersion);
}
public static class RemoteVersionComparator implements Comparator<RemoteVersion<?>> {
public static final RemoteVersionComparator INSTANCE = new RemoteVersionComparator();
private RemoteVersionComparator() {
}
@Override
public int compare(RemoteVersion<?> o1, RemoteVersion<?> o2) {
return -VersionNumber.asVersion(o1.selfVersion).compareTo(VersionNumber.asVersion(o2.selfVersion));
}
}
}

View File

@@ -15,39 +15,48 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.task.Task
import org.jackhuang.hmcl.util.SimpleMultimap
import java.util.*
import kotlin.collections.HashMap
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Optional;
import java.util.TreeSet;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SimpleMultimap;
/**
* The remote version list.
* @param T The type of RemoteVersion<T>, the type of tags.
*
* @param T The type of {@code RemoteVersion<T>}, the type of tags.
*
* @author huangyuhui
*/
abstract class VersionList<T> {
public abstract class VersionList<T> {
/**
* the remote version list.
* key: game version.
* values: corresponding remote versions.
*/
protected val versions = SimpleMultimap<String, RemoteVersion<T>>(::HashMap, ::TreeSet)
protected final SimpleMultimap<String, RemoteVersion<T>> versions = new SimpleMultimap<String, RemoteVersion<T>>(HashMap::new, TreeSet::new);
/**
* True if the version list has been loaded.
*/
val loaded = versions.isNotEmpty
public boolean isLoaded() {
return !versions.isEmpty();
}
/**
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
abstract fun refreshAsync(downloadProvider: DownloadProvider): Task
public abstract Task refreshAsync(DownloadProvider downloadProvider);
private fun getVersionsImpl(gameVersion: String): Collection<RemoteVersion<T>> {
val ans = versions[gameVersion]
return if (ans.isEmpty()) versions.values else ans
private Collection<RemoteVersion<T>> getVersionsImpl(String gameVersion) {
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
return ans.isEmpty() ? versions.values() : ans;
}
/**
@@ -56,8 +65,8 @@ abstract class VersionList<T> {
* @param gameVersion the Minecraft version that remote versions belong to
* @return the collection of specific remote versions
*/
fun getVersions(gameVersion: String): Collection<RemoteVersion<T>> {
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion))
public final Collection<RemoteVersion<T>> getVersions(String gameVersion) {
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion));
}
/**
@@ -67,13 +76,11 @@ abstract class VersionList<T> {
* @param remoteVersion the version of the remote version.
* @return the specific remote version, null if it is not found.
*/
fun getVersion(gameVersion: String, remoteVersion: String): RemoteVersion<T>? {
var result : RemoteVersion<T>? = null
versions[gameVersion].forEach {
if (it.selfVersion == remoteVersion)
result = it
}
return result
public final Optional<RemoteVersion<T>> getVersion(String gameVersion, String remoteVersion) {
RemoteVersion<T> result = null;
for (RemoteVersion<T> it : versions.get(gameVersion))
if (remoteVersion.equals(it.getSelfVersion()))
result = it;
return Optional.ofNullable(result);
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.SimpleVersionProvider;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.IOUtils;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
*
* @author huangyuhui
*/
public final class ForgeInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final VersionList<?> forgeVersionList;
private final File installer = new File("forge-installer.jar").getAbsoluteFile();
private RemoteVersion<?> remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private Task downloadFileTask() {
remote = forgeVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote forge version " + gameVersion + ", " + remoteVersion + " not found"));
return new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer);
}
public ForgeInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
forgeVersionList = dependencyManager.getVersionList("forge");
if (!forgeVersionList.isLoaded())
dependents.add(forgeVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(s -> downloadFileTask()));
else
dependents.add(downloadFileTask());
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public List<Task> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public boolean isRelyingOnDependencies() {
return false;
}
@Override
public void execute() throws Exception {
try (ZipFile zipFile = new ZipFile(installer)) {
InputStream stream = zipFile.getInputStream(zipFile.getEntry("install_profile.json"));
if (stream == null)
throw new IOException("Malformed forge installer file, install_profile.json does not exist.");
String json = IOUtils.readFullyAsString(stream);
InstallProfile installProfile = Constants.GSON.fromJson(json, InstallProfile.class);
if (installProfile == null)
throw new IOException("Malformed forge installer file, install_profile.json does not exist.");
// unpack the universal jar in the installer file.
Library forgeLibrary = Library.fromName(installProfile.getInstall().getPath());
File forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary);
if (!FileUtils.makeFile(forgeFile))
throw new IOException("Cannot make directory " + forgeFile.getParent());
ZipEntry forgeEntry = zipFile.getEntry(installProfile.getInstall().getFilePath());
try (InputStream is = zipFile.getInputStream(forgeEntry); OutputStream os = new FileOutputStream(forgeFile)) {
IOUtils.copyTo(is, os);
}
// resolve the version
SimpleVersionProvider provider = new SimpleVersionProvider();
provider.addVersion(version);
setResult(installProfile.getVersionInfo()
.setInheritsFrom(version.getId())
.resolve(provider)
.setId(version.getId()).setLogging(Collections.EMPTY_MAP));
dependencies.add(new GameLibrariesTask(dependencyManager, installProfile.getVersionInfo()));
}
if (!installer.delete())
throw new IOException("Unable to delete installer file" + installer);
}
}

View File

@@ -0,0 +1,91 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
@Immutable
public final class ForgeVersion implements Validation {
private final String branch;
private final String mcversion;
private final String jobver;
private final String version;
private final int build;
private final double modified;
private final String[][] files;
public ForgeVersion() {
this(null, null, null, null, 0, 0, null);
}
public ForgeVersion(String branch, String mcversion, String jobver, String version, int build, double modified, String[][] files) {
this.branch = branch;
this.mcversion = mcversion;
this.jobver = jobver;
this.version = version;
this.build = build;
this.modified = modified;
this.files = files;
}
public String getBranch() {
return branch;
}
public String getGameVersion() {
return mcversion;
}
public String getJobver() {
return jobver;
}
public String getVersion() {
return version;
}
public int getBuild() {
return build;
}
public double getModified() {
return modified;
}
public String[][] getFiles() {
return files;
}
@Override
public void validate() throws JsonParseException {
if (files == null)
throw new JsonParseException("ForgeVersion files cannot be null");
if (version == null)
throw new JsonParseException("ForgeVersion version cannot be null");
if (mcversion == null)
throw new JsonParseException("ForgeVersion mcversion cannot be null");
}
}

View File

@@ -0,0 +1,93 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.VersionNumber;
/**
*
* @author huangyuhui
*/
public final class ForgeVersionList extends VersionList<Void> {
public static final ForgeVersionList INSTANCE = new ForgeVersionList();
private ForgeVersionList() {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
final List<Task> dependents = Collections.singletonList(task);
return new Task() {
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public void execute() throws Exception {
ForgeVersionRoot root = Constants.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
if (root == null)
return;
versions.clear();
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
String gameVersion = VersionNumber.parseVersion(entry.getKey());
if (gameVersion == null)
continue;
for (int v : entry.getValue()) {
ForgeVersion version = root.getNumber().get(v);
if (version == null)
continue;
String jar = null;
for (String[] file : version.getFiles())
if (file.length > 1 && "installer".equals(file[1])) {
String classifier = version.getGameVersion() + "-" + version.getVersion()
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
jar = downloadProvider.injectURL(root.getWebPath() + classifier + "/" + fileName);
}
if (jar == null)
continue;
versions.put(gameVersion, new RemoteVersion<>(
version.getGameVersion(), version.getVersion(), jar, null
));
}
}
}
};
}
public static final String FORGE_LIST = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
}

View File

@@ -0,0 +1,102 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import com.google.gson.JsonParseException;
import java.util.Map;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
@Immutable
public final class ForgeVersionRoot implements Validation {
private final String artifact;
private final String webpath;
private final String adfly;
private final String homepage;
private final String name;
private final Map<String, int[]> branches;
private final Map<String, int[]> mcversion;
private final Map<String, Integer> promos;
private final Map<Integer, ForgeVersion> number;
public ForgeVersionRoot() {
this(null, null, null, null, null, null, null, null, null);
}
public ForgeVersionRoot(String artifact, String webpath, String adfly, String homepage, String name, Map<String, int[]> branches, Map<String, int[]> mcversion, Map<String, Integer> promos, Map<Integer, ForgeVersion> number) {
this.artifact = artifact;
this.webpath = webpath;
this.adfly = adfly;
this.homepage = homepage;
this.name = name;
this.branches = branches;
this.mcversion = mcversion;
this.promos = promos;
this.number = number;
}
public String getArtifact() {
return artifact;
}
public String getWebPath() {
return webpath;
}
public String getAdfly() {
return adfly;
}
public String getHomePage() {
return homepage;
}
public String getName() {
return name;
}
public Map<String, int[]> getBranches() {
return branches;
}
public Map<String, int[]> getGameVersions() {
return mcversion;
}
public Map<String, Integer> getPromos() {
return promos;
}
public Map<Integer, ForgeVersion> getNumber() {
return number;
}
@Override
public void validate() throws JsonParseException {
if (number == null)
throw new JsonParseException("ForgeVersionRoot number cannot be null");
if (mcversion == null)
throw new JsonParseException("ForgeVersionRoot mcversion cannot be null");
}
}

View File

@@ -0,0 +1,91 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class Install {
private final String profileName;
private final String target;
private final String path;
private final String version;
private final String filePath;
private final String welcome;
private final String minecraft;
private final String mirrorList;
private final String logo;
public Install() {
this(null, null, null, null, null, null, null, null, null);
}
public Install(String profileName, String target, String path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) {
this.profileName = profileName;
this.target = target;
this.path = path;
this.version = version;
this.filePath = filePath;
this.welcome = welcome;
this.minecraft = minecraft;
this.mirrorList = mirrorList;
this.logo = logo;
}
public String getProfileName() {
return profileName;
}
public String getTarget() {
return target;
}
public String getPath() {
return path;
}
public String getVersion() {
return version;
}
public String getFilePath() {
return filePath;
}
public String getWelcome() {
return welcome;
}
public String getMinecraft() {
return minecraft;
}
public String getMirrorList() {
return mirrorList;
}
public String getLogo() {
return logo;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
@Immutable
public final class InstallProfile implements Validation {
@SerializedName("install")
private final Install install;
@SerializedName("versionInfo")
private final Version versionInfo;
public InstallProfile(Install install, Version versionInfo) {
this.install = install;
this.versionInfo = versionInfo;
}
public Install getInstall() {
return install;
}
public Version getVersionInfo() {
return versionInfo;
}
@Override
public void validate() throws JsonParseException {
if (install == null)
throw new JsonParseException("InstallProfile install cannot be null");
if (versionInfo == null)
throw new JsonParseException("InstallProfile versionInfo cannot be null");
}
}

View File

@@ -0,0 +1,111 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.AssetIndexInfo;
import org.jackhuang.hmcl.game.AssetObject;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.DigestUtils;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
*
* @author huangyuhui
*/
public final class GameAssetDownloadTask extends Task {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final GameAssetRefreshTask refreshTask;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version;
this.refreshTask = new GameAssetRefreshTask(dependencyManager, version);
this.dependents.add(refreshTask);
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public List<Task> getDependencies() {
return dependencies;
}
@Override
public void execute() throws Exception {
int size = refreshTask.getResult().size();
for (Map.Entry<File, AssetObject> entry : refreshTask.getResult()) {
File file = entry.getKey();
AssetObject assetObject = entry.getValue();
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) {
Logging.LOG.log(Level.SEVERE, "Unable to create new file {0}, because parent directory cannot be created", file);
continue;
}
if (file.isDirectory())
continue;
boolean flag = true;
int downloaded = 0;
try {
// check the checksum of file to ensure that the file is not need to re-download.
if (file.exists()) {
String sha1 = DigestUtils.sha1Hex(FileUtils.readBytes(file));
if (sha1.equals(assetObject.getHash())) {
++downloaded;
Logging.LOG.finest("File $file has been downloaded successfully, skipped downloading");
updateProgress(downloaded, size);
continue;
}
}
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to get hash code of file " + file, e);
flag = !file.exists();
}
if (flag) {
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), assetObject.getHash());
task.setName(assetObject.getHash());
dependencies.add(task);
}
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.AssetIndexInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
* This task is to download asset index file provided in minecraft.json.
*
* @author huangyuhui
*/
public final class GameAssetIndexDownloadTask extends Task {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version;
}
@Override
public List<Task> getDependencies() {
return dependencies;
}
@Override
public void execute() throws Exception {
AssetIndexInfo assetIndexInfo = version.getAssetIndex();
File assetDir = dependencyManager.getGameRepository().getAssetDirectory(version.getId(), assetIndexInfo.getId());
if (FileUtils.makeDirectory(assetDir))
throw new IOException("Cannot create directory: " + assetDir);
File assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(assetIndexInfo.getUrl())),
assetIndexFile, dependencyManager.getProxy()
));
}
}

View File

@@ -0,0 +1,88 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.AssetIndex;
import org.jackhuang.hmcl.game.AssetIndexInfo;
import org.jackhuang.hmcl.game.AssetObject;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Pair;
/**
* This task is to extract all asset objects described in asset index json.
*
* @author huangyuhui
*/
public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File, AssetObject>>> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final AssetIndexInfo assetIndexInfo;
private final File assetIndexFile;
private final List<Task> dependents = new LinkedList<>();
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version;
this.assetIndexInfo = version.getAssetIndex();
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
if (!assetIndexFile.exists())
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, version));
}
@Override
public List<Task> getDependents() {
return dependents;
}
@Override
public String getId() {
return ID;
}
@Override
public void execute() throws Exception {
AssetIndex index = Constants.GSON.fromJson(FileUtils.readText(assetIndexFile), AssetIndex.class);
List<Pair<File, AssetObject>> res = new LinkedList<>();
int progress = 0;
if (index != null)
for (AssetObject assetObject : index.getObjects().values()) {
res.add(new Pair<>(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject));
updateProgress(++progress, index.getObjects().size());
}
setResult(res);
}
public static final String ID = "game_asset_refresh_task";
}

View File

@@ -0,0 +1,60 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
*
* @author huangyuhui
*/
public final class GameDownloadTask extends Task {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
public GameDownloadTask(DefaultDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version;
}
@Override
public List<Task> getDependencies() {
return dependencies;
}
@Override
public void execute() throws Exception {
File jar = dependencyManager.getGameRepository().getVersionJar(version);
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
jar,
dependencyManager.getProxy(),
version.getDownloadInfo().getSha1()
));
}
}

View File

@@ -0,0 +1,70 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
* This task is to download game libraries.
* This task should be executed last(especially after game downloading, Forge, LiteLoader and OptiFine install task).
*
* @author huangyuhui
*/
public final class GameLibrariesTask extends Task {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version;
}
@Override
public List<Task> getDependencies() {
return dependencies;
}
@Override
public void execute() throws Exception {
for (Library library : version.getLibraries())
if (library.appliesToCurrentEnvironment()) {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (!file.exists())
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl())),
file, dependencyManager.getProxy(), library.getDownload().getSha1()));
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.DependencyManager;
import org.jackhuang.hmcl.game.DownloadType;
import org.jackhuang.hmcl.game.LoggingInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
* This task is to download log4j configuration file provided in minecraft.json.
*
* @author huangyuhui
*/
public final class GameLoggingDownloadTask extends Task {
private final DependencyManager dependencyManager;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version;
}
@Override
public Collection<Task> getDependencies() {
return dependencies;
}
@Override
public void execute() throws Exception {
if (version.getLogging() == null || !version.getLogging().containsKey(DownloadType.CLIENT))
return;
LoggingInfo logging = version.getLogging().get(DownloadType.CLIENT);
File file = dependencyManager.getGameRepository().getLoggingObject(version.getId(), version.getAssetIndex().getId(), logging);
if (!file.exists())
dependencies.add(new FileDownloadTask(NetworkUtils.toURL(logging.getFile().getUrl()), file, dependencyManager.getProxy()));
}
}

View File

@@ -0,0 +1,52 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class GameRemoteLatestVersions {
@SerializedName("snapshot")
private final String snapshot;
@SerializedName("release")
private final String release;
public GameRemoteLatestVersions() {
this(null, null);
}
public GameRemoteLatestVersions(String snapshot, String release) {
this.snapshot = snapshot;
this.release = release;
}
public String getRelease() {
return release;
}
public String getSnapshot() {
return snapshot;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import org.jackhuang.hmcl.game.ReleaseType;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
public final class GameRemoteVersion implements Validation {
@SerializedName("id")
private final String gameVersion;
@SerializedName("time")
private final Date time;
@SerializedName("releaseTime")
private final Date releaseTime;
@SerializedName("type")
private final ReleaseType type;
@SerializedName("url")
private final String url;
public GameRemoteVersion() {
this("", new Date(), new Date(), ReleaseType.UNKNOWN);
}
public GameRemoteVersion(String gameVersion, Date time, Date releaseTime, ReleaseType type) {
this(gameVersion, time, releaseTime, type, Constants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json");
}
public GameRemoteVersion(String gameVersion, Date time, Date releaseTime, ReleaseType type, String url) {
this.gameVersion = gameVersion;
this.time = time;
this.releaseTime = releaseTime;
this.type = type;
this.url = url;
}
public String getGameVersion() {
return gameVersion;
}
public Date getTime() {
return time;
}
public Date getReleaseTime() {
return releaseTime;
}
public ReleaseType getType() {
return type;
}
public String getUrl() {
return url;
}
@Override
public void validate() throws JsonParseException {
if (StringUtils.isBlank(gameVersion))
throw new JsonParseException("GameRemoteVersion id cannot be blank");
if (StringUtils.isBlank(url))
throw new JsonParseException("GameRemoteVersion url cannot be blank");
}
}

View File

@@ -15,27 +15,36 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game
package org.jackhuang.hmcl.download.game;
import com.google.gson.annotations.SerializedName
import org.jackhuang.hmcl.util.Immutable
import org.jackhuang.hmcl.util.Validation
import java.util.Date;
import org.jackhuang.hmcl.game.ReleaseType;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
class LoggingInfo @JvmOverloads constructor(
@SerializedName("file")
val file: IdDownloadInfo = IdDownloadInfo(),
public final class GameRemoteVersionTag {
@SerializedName("argument")
val argument: String = "",
private final ReleaseType type;
private final Date time;
@SerializedName("type")
val type: String = ""
): Validation {
override fun validate() {
file.validate()
require(argument.isNotBlank(), { "LoggingInfo" })
require(type.isNotBlank())
public GameRemoteVersionTag() {
this(ReleaseType.UNKNOWN, new Date());
}
}
public GameRemoteVersionTag(ReleaseType type, Date time) {
this.type = type;
this.time = time;
}
public Date getTime() {
return time;
}
public ReleaseType getType() {
return type;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class GameRemoteVersions {
@SerializedName("versions")
private final List<GameRemoteVersion> versions;
@SerializedName("latest")
private final GameRemoteLatestVersions latest;
public GameRemoteVersions() {
this(Collections.EMPTY_LIST, null);
}
public GameRemoteVersions(List<GameRemoteVersion> versions, GameRemoteLatestVersions latest) {
this.versions = versions;
this.latest = latest;
}
public GameRemoteLatestVersions getLatest() {
return latest;
}
public List<GameRemoteVersion> getVersions() {
return versions;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.util.Collection;
import java.util.Collections;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.jackhuang.hmcl.util.VersionNumber;
/**
*
* @author huangyuhui
*/
public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
public static final GameVersionList INSTANCE = new GameVersionList();
private GameVersionList() {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()));
return new Task() {
@Override
public Collection<Task> getDependents() {
return Collections.singleton(task);
}
@Override
public void execute() throws Exception {
versions.clear();
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
for (GameRemoteVersion remoteVersion : root.getVersions()) {
String gameVersion = VersionNumber.parseVersion(remoteVersion.getGameVersion());
if (gameVersion == null)
continue;
versions.put(gameVersion, new RemoteVersion<>(
remoteVersion.getGameVersion(),
remoteVersion.getGameVersion(),
remoteVersion.getUrl(),
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
);
}
}
};
}
}

View File

@@ -0,0 +1,70 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.net.Proxy;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
*
* @author huangyuhui
*/
public final class VersionJsonDownloadTask extends Task {
private final String gameVersion;
private final DefaultDependencyManager dependencyManager;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final VersionList<?> gameVersionList;
public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) {
this.gameVersion = gameVersion;
this.dependencyManager = dependencyManager;
this.gameVersionList = dependencyManager.getVersionList("game");
if (!gameVersionList.isLoaded())
dependents.add(gameVersionList.refreshAsync(dependencyManager.getDownloadProvider()));
}
@Override
public Collection<Task> getDependencies() {
return dependencies;
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public void execute() throws Exception {
RemoteVersion<?> remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot find specific version "+gameVersion+" in remote repository"));
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), Proxy.NO_PROXY, ID));
}
public static final String ID = "raw_version_json";
}

View File

@@ -0,0 +1,56 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.game;
import java.io.File;
import java.io.IOException;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
/**
* This task is to save the version json.
*
* @author huangyuhui
*/
public final class VersionJsonSaveTask extends Task {
private final DefaultGameRepository repository;
private final Version version;
/**
* Constructor.
*
* @param repository the game repository
* @param version the **resolved** version
*/
public VersionJsonSaveTask(DefaultGameRepository repository, Version version) {
this.repository = repository;
this.version = version;
}
@Override
public void execute() throws Exception {
File json = repository.getVersionJson(version.getId()).getAbsoluteFile();
if (!FileUtils.makeFile(json))
throw new IOException("Cannot create file " + json);
FileUtils.writeText(json, Constants.GSON.toJson(version));
}
}

View File

@@ -0,0 +1,57 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LiteLoaderBranch {
@SerializedName("libraries")
private final Collection<Library> libraries;
@SerializedName("com.mumfrey:liteloader")
private final Map<String, LiteLoaderVersion> liteLoader;
public LiteLoaderBranch() {
this(Collections.EMPTY_SET, Collections.EMPTY_MAP);
}
public LiteLoaderBranch(Collection<Library> libraries, Map<String, LiteLoaderVersion> liteLoader) {
this.libraries = libraries;
this.liteLoader = liteLoader;
}
public Collection<Library> getLibraries() {
return Collections.unmodifiableCollection(libraries);
}
public Map<String, LiteLoaderVersion> getLiteLoader() {
return Collections.unmodifiableMap(liteLoader);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LiteLoaderGameVersions {
@SerializedName("repo")
private final LiteLoaderRepository repoitory;
@SerializedName("artefacts")
private final LiteLoaderBranch artifacts;
@SerializedName("snapshots")
private final LiteLoaderBranch snapshots;
public LiteLoaderGameVersions() {
this(null, null, null);
}
public LiteLoaderGameVersions(LiteLoaderRepository repoitory, LiteLoaderBranch artifacts, LiteLoaderBranch snapshots) {
this.repoitory = repoitory;
this.artifacts = artifacts;
this.snapshots = snapshots;
}
public LiteLoaderRepository getRepoitory() {
return repoitory;
}
public LiteLoaderBranch getArtifacts() {
return artifacts;
}
public LiteLoaderBranch getSnapshots() {
return snapshots;
}
}

View File

@@ -0,0 +1,111 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.Arguments;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
/**
* Note: LiteLoader must be installed after Forge.
*
* @author huangyuhui
*/
public final class LiteLoaderInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final LiteLoaderVersionList liteLoaderVersionList;
private RemoteVersion<LiteLoaderRemoteVersionTag> remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private void doRemote() {
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote LiteLoader version " + gameVersion + ", " + remoteVersion + " not found"));
}
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
liteLoaderVersionList = (LiteLoaderVersionList) dependencyManager.getVersionList("liteloader");
if (!liteLoaderVersionList.isLoaded())
dependents.add(liteLoaderVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(s -> {
doRemote();
return null;
}));
else
doRemote();
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public void execute() throws Exception {
Library library = new Library(
"com.mumfrey", "liteloader", remote.getSelfVersion(), null,
"http://dl.liteloader.com/versions/",
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
);
Version tempVersion = version.setLibraries(Lang.merge(remote.getTag().getLibraries(), Arrays.asList(library)));
setResult(version
.setMainClass("net.minecraft.launchwrapper.Launch")
.setLibraries(Lang.merge(tempVersion.getLibraries(), version.getLibraries()))
.setLogging(Collections.EMPTY_MAP)
.setMinecraftArguments(version.getMinecraftArguments().orElse("") + " --tweakClass " + remote.getTag().getTweakClass())
//.setArguments(Arguments.addGameArguments(Lang.get(version.getArguments()), "--tweakClass", remote.getTag().getTweakClass()))
);
dependencies.add(new GameLibrariesTask(dependencyManager, tempVersion));
}
}

View File

@@ -0,0 +1,49 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import java.util.Collection;
import java.util.Collections;
import org.jackhuang.hmcl.game.Library;
/**
*
* @author huangyuhui
*/
public final class LiteLoaderRemoteVersionTag {
private final String tweakClass;
private final Collection<Library> libraries;
public LiteLoaderRemoteVersionTag() {
this("", Collections.EMPTY_SET);
}
public LiteLoaderRemoteVersionTag(String tweakClass, Collection<Library> libraries) {
this.tweakClass = tweakClass;
this.libraries = libraries;
}
public Collection<Library> getLibraries() {
return libraries;
}
public String getTweakClass() {
return tweakClass;
}
}

View File

@@ -0,0 +1,69 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LiteLoaderRepository {
@SerializedName("stream")
private final String stream;
@SerializedName("type")
private final String type;
@SerializedName("url")
private final String url;
@SerializedName("classifier")
private final String classifier;
public LiteLoaderRepository() {
this("", "", "", "");
}
public LiteLoaderRepository(String stream, String type, String url, String classifier) {
this.stream = stream;
this.type = type;
this.url = url;
this.classifier = classifier;
}
public String getStream() {
return stream;
}
public String getType() {
return type;
}
public String getUrl() {
return url;
}
public String getClassifier() {
return classifier;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import java.util.Collection;
import java.util.Collections;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LiteLoaderVersion {
private final String tweakClass;
private final String file;
private final String version;
private final String md5;
private final String timestamp;
private final int lastSuccessfulBuild;
private final Collection<Library> libraries;
public LiteLoaderVersion() {
this("", "", "", "", "", 0, Collections.EMPTY_SET);
}
public LiteLoaderVersion(String tweakClass, String file, String version, String md5, String timestamp, int lastSuccessfulBuild, Collection<Library> libraries) {
this.tweakClass = tweakClass;
this.file = file;
this.version = version;
this.md5 = md5;
this.timestamp = timestamp;
this.lastSuccessfulBuild = lastSuccessfulBuild;
this.libraries = libraries;
}
public String getTweakClass() {
return tweakClass;
}
public String getFile() {
return file;
}
public String getVersion() {
return version;
}
public String getMd5() {
return md5;
}
public String getTimestamp() {
return timestamp;
}
public int getLastSuccessfulBuild() {
return lastSuccessfulBuild;
}
public Collection<Library> getLibraries() {
return Collections.unmodifiableCollection(libraries);
}
}

View File

@@ -0,0 +1,91 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.jackhuang.hmcl.util.VersionNumber;
/**
*
* @author huangyuhui
*/
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersionTag> {
public static final LiteLoaderVersionList INSTANCE = new LiteLoaderVersionList();
private LiteLoaderVersionList() {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() {
@Override
public Collection<Task> getDependents() {
return Collections.singleton(task);
}
@Override
public void execute() throws Exception {
LiteLoaderVersionsRoot root = Constants.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
versions.clear();
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
String gg = VersionNumber.parseVersion(gameVersion);
if (gg == null)
continue;
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
}
}
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
if (branch == null || repository == null)
return;
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
String branchName = entry.getKey();
LiteLoaderVersion v = entry.getValue();
if ("latest".equals(branchName))
continue;
versions.put(key, new RemoteVersion<>(gameVersion,
v.getVersion().replace("SNAPSHOT", "SNAPSHOT-" + v.getLastSuccessfulBuild()),
snapshot
? "http://jenkins.liteloader.com/LiteLoader " + gameVersion + "/lastSuccessfulBuild/artifact/build/libs/liteloader-" + v.getVersion() + "-release.jar"
: downloadProvider.injectURL(repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile()),
new LiteLoaderRemoteVersionTag(v.getTweakClass(), v.getLibraries())
));
}
}
};
}
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
}

View File

@@ -0,0 +1,61 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LiteLoaderVersionsMeta {
@SerializedName("description")
private final String description;
@SerializedName("authors")
private final String authors;
@SerializedName("url")
private final String url;
public LiteLoaderVersionsMeta() {
this("", "", "");
}
public LiteLoaderVersionsMeta(String description, String authors, String url) {
this.description = description;
this.authors = authors;
this.url = url;
}
public String getDescription() {
return description;
}
public String getAuthors() {
return authors;
}
public String getUrl() {
return url;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.Map;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LiteLoaderVersionsRoot {
@SerializedName("versions")
private final Map<String, LiteLoaderGameVersions> versions;
@SerializedName("meta")
private final LiteLoaderVersionsMeta meta;
public LiteLoaderVersionsRoot() {
this(Collections.EMPTY_MAP, null);
}
public LiteLoaderVersionsRoot(Map<String, LiteLoaderGameVersions> versions, LiteLoaderVersionsMeta meta) {
this.versions = versions;
this.meta = meta;
}
public Map<String, LiteLoaderGameVersions> getVersions() {
return Collections.unmodifiableMap(versions);
}
public LiteLoaderVersionsMeta getMeta() {
return meta;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.optifine;
import com.google.gson.reflect.TypeToken;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.VersionNumber;
/**
*
* @author huangyuhui
*/
public final class OptiFineBMCLVersionList extends VersionList<Void> {
public static final OptiFineBMCLVersionList INSTANCE = new OptiFineBMCLVersionList();
private OptiFineBMCLVersionList() {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi.bangbang93.com/optifine/versionlist"));
return new Task() {
@Override
public Collection<Task> getDependents() {
return Collections.singleton(task);
}
@Override
public void execute() throws Exception {
versions.clear();
Set<String> duplicates = new HashSet<>();
List<OptiFineVersion> root = Constants.GSON.fromJson(task.getResult(), new TypeToken<List<OptiFineVersion>>() {
}.getType());
for (OptiFineVersion element : root) {
String version = element.getType();
if (version == null)
continue;
String mirror = "http://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
if (!duplicates.add(mirror))
continue;
if (StringUtils.isBlank(element.getGameVersion()))
continue;
String gameVersion = VersionNumber.parseVersion(element.getGameVersion());
versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, mirror, null));
}
}
};
}
}

View File

@@ -0,0 +1,56 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.optifine;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.NetworkUtils;
/**
*
* @author huangyuhui
*/
public final class OptiFineDownloadFormatter extends TaskResult<String> {
private final String url;
public OptiFineDownloadFormatter(String url) {
this.url = url;
}
@Override
public void execute() throws Exception {
String result = null;
String content = NetworkUtils.doGet(NetworkUtils.toURL(url));
Matcher m = PATTERN.matcher(content);
while (m.find())
result = m.group(1);
if (result == null)
throw new IllegalStateException("Cannot find version in " + content);
setResult("http://optifine.net/downloadx?f=OptiFine" + result);
}
@Override
public String getId() {
return ID;
}
public static final String ID = "optifine_formatter";
private static final Pattern PATTERN = Pattern.compile("\"downloadx\\?f=OptiFine(.*)\"");
}

View File

@@ -0,0 +1,142 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.optifine;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.Argument;
import org.jackhuang.hmcl.game.Arguments;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.StringArgument;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
/**
* <b>Note</b>: OptiFine should be installed in the end.
*
* @author huangyuhui
*/
public final class OptiFineInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final VersionList<?> optiFineVersionList;
private RemoteVersion<?> remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private void doRemote() {
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote OptiFine version " + gameVersion + ", " + remoteVersion + " not found"));
}
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
optiFineVersionList = dependencyManager.getVersionList("optifine");
if (!optiFineVersionList.isLoaded())
dependents.add(optiFineVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(s -> {
doRemote();
return null;
}));
else
doRemote();
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public boolean isRelyingOnDependencies() {
return false;
}
@Override
public void execute() throws Exception {
Library library = new Library(
"net.optifine", "optifine", remoteVersion, null, null,
new LibrariesDownloadInfo(new LibraryDownloadInfo(
"net/optifine/optifine/" + remoteVersion + "/optifine-" + remoteVersion + ".jar",
remote.getUrl())), true
);
List<Library> libraries = new LinkedList<>();
libraries.add(library);
boolean hasFMLTweaker = false;
if (version.getMinecraftArguments().isPresent() && version.getMinecraftArguments().get().contains("FMLTweaker"))
hasFMLTweaker = true;
if (version.getArguments().isPresent()) {
List<Argument> game = version.getArguments().get().getGame();
if (game.stream().anyMatch(arg -> arg.toString(Collections.EMPTY_MAP, Collections.EMPTY_MAP).contains("FMLTweaker")))
hasFMLTweaker = true;
}
/*Arguments arguments = Lang.get(version.getArguments());
if (!hasFMLTweaker)
arguments = Arguments.addGameArguments(arguments, "--tweakClass", "optifine.OptiFineTweaker");
*/
String minecraftArguments = version.getMinecraftArguments().orElse("");
if (!hasFMLTweaker)
minecraftArguments = minecraftArguments + " --tweakClass optifine.OptiFineTweaker";
if (version.getMainClass() == null || !version.getMainClass().startsWith("net.minecraft.launchwrapper."))
libraries.add(0, new Library("net.minecraft", "launchwrapper", "1.12"));
setResult(version
.setLibraries(Lang.merge(version.getLibraries(), libraries))
.setMainClass("net.minecraft.launchwrapper.Launch")
.setMinecraftArguments(minecraftArguments)
//.setArguments(arguments)
);
dependencies.add(new GameLibrariesTask(dependencyManager, version.setLibraries(libraries)));
}
}

View File

@@ -0,0 +1,90 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.optifine;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huangyuhui
*/
public final class OptiFineVersion {
@SerializedName("dl")
private final String downloadLink;
@SerializedName("ver")
private final String version;
@SerializedName("date")
private final String date;
@SerializedName("type")
private final String type;
@SerializedName("patch")
private final String patch;
@SerializedName("mirror")
private final String mirror;
@SerializedName("mcversion")
private final String gameVersion;
public OptiFineVersion() {
this(null, null, null, null, null, null, null);
}
public OptiFineVersion(String downloadLink, String version, String date, String type, String patch, String mirror, String gameVersion) {
this.downloadLink = downloadLink;
this.version = version;
this.date = date;
this.type = type;
this.patch = patch;
this.mirror = mirror;
this.gameVersion = gameVersion;
}
public String getDownloadLink() {
return downloadLink;
}
public String getVersion() {
return version;
}
public String getDate() {
return date;
}
public String getType() {
return type;
}
public String getPatch() {
return patch;
}
public String getMirror() {
return mirror;
}
public String getGameVersion() {
return gameVersion;
}
}

View File

@@ -0,0 +1,96 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.optifine;
import java.io.ByteArrayInputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
*
* @author huangyuhui
*/
public final class OptiFineVersionList extends VersionList<Void> {
private static final Pattern PATTERN = Pattern.compile("OptiFine (.*?) ");
public static final OptiFineVersionList INSTANCE = new OptiFineVersionList();
private OptiFineVersionList() {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL("http://optifine.net/downloads"));
return new Task() {
@Override
public Collection<Task> getDependents() {
return Collections.singleton(task);
}
@Override
public void execute() throws Exception {
versions.clear();
String html = task.getResult().replace("&nbsp;", " ").replace("&gt;", ">").replace("&lt;", "<").replace("<br>", "<br />");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
Element r = doc.getDocumentElement();
NodeList tables = r.getElementsByTagName("table");
for (int i = 0; i < tables.getLength(); i++) {
Element e = (Element) tables.item(i);
if ("downloadTable".equals(e.getAttribute("class"))) {
NodeList tr = e.getElementsByTagName("tr");
for (int k = 0; k < tr.getLength(); k++) {
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
String url = null, version = null, gameVersion = null;
for (int j = 0; j < downloadLine.getLength(); j++) {
Element td = (Element) downloadLine.item(j);
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
version = td.getTextContent();
}
Matcher matcher = PATTERN.matcher(version);
while (matcher.find())
gameVersion = matcher.group(1);
if (gameVersion == null || version == null || url == null)
continue;
versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, url, null));
}
}
}
}
};
}
}

View File

@@ -0,0 +1,85 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
/**
*
* @author huangyuhui
*/
public class Event extends EventObject {
public Event(Object source) {
super(source);
}
private boolean canceled;
/**
* true if this event is canceled.
*
* @throws UnsupportedOperationException if trying to cancel a non-cancelable event.
*/
public final boolean isCanceled() {
return canceled;
}
/**
*
* @param canceled new value
* @throws UnsupportedOperationException if trying to cancel a non-cancelable event.
*/
public final void setCanceled(boolean canceled) {
if (!isCancelable())
throw new UnsupportedOperationException("Attempted to cancel a non-cancelable event: " + getClass());
this.canceled = canceled;
}
/**
* true if this Event this cancelable.
*/
public boolean isCancelable() {
return false;
}
public boolean hasResult() {
return false;
}
private Result result;
/**
* Retutns the value set as the result of this event
*/
public Result getResult() {
return result;
}
public void setResult(Result result) {
if (!hasResult())
throw new UnsupportedOperationException("Attempted to set result on a no result event: " + this.getClass() + " of type.");
this.result = result;
}
public enum Result {
DENY,
DEFAULT,
ALLOW
}
}

View File

@@ -0,0 +1,43 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
import java.util.HashMap;
import org.jackhuang.hmcl.task.Schedulers;
/**
*
* @author huangyuhui
*/
public final class EventBus {
private final HashMap<Class<?>, EventManager<?>> events = new HashMap<>();
public <T extends EventObject> EventManager<T> channel(Class<T> clazz) {
if (!events.containsKey(clazz))
events.put(clazz, new EventManager<>(Schedulers.computation()));
return (EventManager<T>) events.get(clazz);
}
public void fireEvent(EventObject obj) {
channel((Class<EventObject>) obj.getClass()).fireEvent(obj);
}
public static final EventBus EVENT_BUS = new EventBus();
}

View File

@@ -0,0 +1,85 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EnumMap;
import java.util.EventObject;
import java.util.HashSet;
import java.util.function.Consumer;
import org.jackhuang.hmcl.task.Scheduler;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.SimpleMultimap;
/**
*
* @author huangyuhui
*/
public final class EventManager<T extends EventObject> {
private final Scheduler scheduler;
private final SimpleMultimap<EventPriority, Consumer<T>> handlers
= new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new);
private final SimpleMultimap<EventPriority, Runnable> handlers2
= new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new);
public EventManager() {
this(Schedulers.immediate());
}
public EventManager(Scheduler scheduler) {
this.scheduler = scheduler;
}
public void register(Consumer<T> consumer) {
register(consumer, EventPriority.NORMAL);
}
public void register(Consumer<T> consumer, EventPriority priority) {
if (!handlers.get(priority).contains(consumer))
handlers.put(priority, consumer);
}
public void register(Runnable runnable) {
register(runnable, EventPriority.NORMAL);
}
public void register(Runnable runnable, EventPriority priority) {
if (!handlers2.get(priority).contains(runnable))
handlers2.put(priority, runnable);
}
public void unregister(Consumer<T> consumer) {
handlers.removeValue(consumer);
}
public void unregister(Runnable runnable) {
handlers2.removeValue(runnable);
}
public void fireEvent(T event) {
scheduler.schedule(() -> {
for (EventPriority priority : EventPriority.values()) {
for (Consumer<T> handler : handlers.get(priority))
handler.accept(event);
for (Runnable runnable : handlers2.get(priority))
runnable.run();
}
});
}
}

View File

@@ -15,12 +15,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event
package org.jackhuang.hmcl.event;
enum class EventPriority {
/**
*
* @author huangyuhui
*/
public enum EventPriority {
HIGHEST,
HIGH,
NORMAL,
LOW,
LOWEST
}
}

View File

@@ -0,0 +1,49 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
/**
*
* @author huang
*/
public class FailedEvent<T> extends EventObject {
private final int failedTime;
private T newResult;
public FailedEvent(Object source, int failedTime, T newResult) {
super(source);
this.failedTime = failedTime;
this.newResult = newResult;
}
public int getFailedTime() {
return failedTime;
}
public T getNewResult() {
return newResult;
}
public void setNewResult(T newResult) {
this.newResult = newResult;
}
}

View File

@@ -15,27 +15,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.task.Scheduler
import java.util.*
import java.util.EventObject;
import org.jackhuang.hmcl.util.ManagedProcess;
class EventBus {
private val events = HashMap<Class<*>, EventManager<*>>()
/**
* This event gets fired when we launch the JVM and it got crashed.
* <br>
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
*
* @param source [org.jackhuang.hmcl.launch.ExitWaiter]
* @param value the crashed process.
* @author huangyuhui
*/
public class JVMLaunchFailedEvent extends EventObject {
@Suppress("UNCHECKED_CAST")
fun <T : EventObject> channel(classOfT: Class<T>): EventManager<T> {
if (!events.containsKey(classOfT))
events.put(classOfT, EventManager<T>(Scheduler.COMPUTATION))
return events[classOfT] as EventManager<T>
private final ManagedProcess process;
public JVMLaunchFailedEvent(Object source, ManagedProcess process) {
super(source);
this.process = process;
}
inline fun <reified T: EventObject> channel() = channel(T::class.java)
fun fireEvent(obj: EventObject) {
channel(obj.javaClass).fireEvent(obj)
public ManagedProcess getProcess() {
return process;
}
}
val EVENT_BUS = EventBus()

View File

@@ -0,0 +1,44 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
/**
* This event gets fired when a minecraft version has been loaded.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the version id.
*
* @author huangyuhui
*/
public final class LoadedOneVersionEvent extends EventObject {
private final String version;
public LoadedOneVersionEvent(Object source, String version) {
super(source);
this.version = version;
}
public String getVersion() {
return version;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
import org.jackhuang.hmcl.util.ManagedProcess;
/**
* This event gets fired when a JavaProcess exited abnormally and the exit code is not zero.
* <br></br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param value The process that exited abnormally.
* @author huangyuhui
*/
public final class ProcessExitedAbnormallyEvent extends EventObject {
private final ManagedProcess process;
public ProcessExitedAbnormallyEvent(Object source, ManagedProcess process) {
super(source);
this.process = process;
}
public ManagedProcess getProcess() {
return process;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
import org.jackhuang.hmcl.util.ManagedProcess;
/**
* This event gets fired when minecraft process exited successfully and the exit code is 0.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.launch.ExitWaiter}
* @param value minecraft process
* @author huangyuhui
*/
public class ProcessStoppedEvent extends EventObject {
private final ManagedProcess process;
public ProcessStoppedEvent(Object source, ManagedProcess process) {
super(source);
this.process = process;
}
public ManagedProcess getProcess() {
return process;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
/**
* This event gets fired when all the versions in .minecraft folder are loaded.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.api.HMCLApi#EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository]
*
* @author huangyuhui
*/
public final class RefreshedVersionsEvent extends EventObject {
public RefreshedVersionsEvent(Object source) {
super(source);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.event;
import java.util.EventObject;
/**
* This event gets fired when loading versions in a .minecraft folder.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EVENT_BUS}
*
* @param source {@link org.jackhuang.hmcl.game.GameRepository}
*
* @author huangyuhui
*/
public final class RefreshingVersionsEvent extends EventObject {
public RefreshingVersionsEvent(Object source) {
super(source);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public interface Argument extends Cloneable {
/**
* Parse this argument in form: ${key name} or simply a string.
*
* @param keys the parse map
* @param features the map that contains some features such as 'is_demo_user', 'has_custom_resolution'
* @return parsed argument element, empty if this argument is ignored and will not be added.
*/
List<String> toString(Map<String, String> keys, Map<String, Boolean> features);
public static class Serializer implements JsonDeserializer<Argument>, JsonSerializer<Argument> {
public static final Serializer INSTANCE = new Serializer();
private Serializer() {
}
@Override
public Argument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive())
return new StringArgument(json.getAsString());
else
return context.deserialize(json, RuledArgument.class);
}
@Override
public JsonElement serialize(Argument src, Type typeOfSrc, JsonSerializationContext context) {
if (src instanceof StringArgument)
return new JsonPrimitive(((StringArgument) src).getArgument());
else if (src instanceof RuledArgument)
return context.serialize(src, RuledArgument.class);
else
throw new AssertionError("Unrecognized argument type: " + src);
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.annotations.SerializedName;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.OperatingSystem;
/**
*
* @author huangyuhui
*/
@Immutable
public final class Arguments {
@SerializedName("game")
private final List<Argument> game;
@SerializedName("jvm")
private final List<Argument> jvm;
public Arguments() {
this(null, null);
}
public Arguments(List<Argument> game, List<Argument> jvm) {
this.game = game;
this.jvm = jvm;
}
public List<Argument> getGame() {
return game == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(game);
}
public List<Argument> getJvm() {
return jvm == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(jvm);
}
public static Arguments addGameArguments(Arguments arguments, String... gameArguments) {
return addGameArguments(arguments, Arrays.asList(gameArguments));
}
public static Arguments addGameArguments(Arguments arguments, List<String> gameArguments) {
List<Argument> list = gameArguments.stream().map(StringArgument::new).collect(Collectors.toList());
if (arguments == null)
return new Arguments(list, null);
else
return new Arguments(Lang.merge(arguments.getGame(), list), arguments.getJvm());
}
public static Arguments merge(Arguments a, Arguments b) {
if (a == null)
return b;
else if (b == null)
return a;
else
return new Arguments(Lang.merge(a.game, b.game), Lang.merge(a.jvm, b.jvm));
}
public static List<String> parseStringArguments(List<String> arguments, Map<String, String> keys) {
return arguments.stream().map(str -> keys.getOrDefault(str, str)).collect(Collectors.toList());
}
public static List<String> parseArguments(List<Argument> arguments, Map<String, String> keys) {
return parseArguments(arguments, keys, Collections.EMPTY_MAP);
}
public static List<String> parseArguments(List<Argument> arguments, Map<String, String> keys, Map<String, Boolean> features) {
return arguments.stream().flatMap(arg -> arg.toString(keys, features).stream()).collect(Collectors.toList());
}
public static final List<Argument> DEFAULT_JVM_ARGUMENTS;
public static final List<Argument> DEFAULT_GAME_ARGUMENTS;
static {
List<Argument> jvm = new LinkedList<>();
jvm.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.OSX))), Collections.singletonList("-XstartOnFirstThread")));
jvm.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.WINDOWS))), Collections.singletonList("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump")));
jvm.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.WINDOWS, "^10\\."))), Arrays.asList("-Dos.name=Windows 10", "-Dos.version=10.0")));
jvm.add(new StringArgument("-Djava.library.path=${natives_directory}"));
jvm.add(new StringArgument("-Dminecraft.launcher.brand=${launcher_name}"));
jvm.add(new StringArgument("-Dminecraft.launcher.version=${launcher_version}"));
jvm.add(new StringArgument("-cp"));
jvm.add(new StringArgument("${classpath}"));
DEFAULT_JVM_ARGUMENTS = Collections.unmodifiableList(jvm);
List<Argument> game = new LinkedList<>();
game.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, null, Collections.singletonMap("has_custom_resolution", true))), Arrays.asList("--width", "${resolution_width}", "--height", "${resolution_height}")));
DEFAULT_GAME_ARGUMENTS = Collections.unmodifiableList(game);
}
}

View File

@@ -15,19 +15,40 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game
package org.jackhuang.hmcl.game;
import com.google.gson.annotations.SerializedName
import java.util.*
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
class AssetIndex @JvmOverloads constructor(
@SerializedName("virtual")
val virtual: Boolean = false,
objects: Map<String, AssetObject> = emptyMap()
) {
val objects: Map<String, AssetObject>
get() = Collections.unmodifiableMap(objectsImpl)
/**
*
* @author huangyuhui
*/
public final class AssetIndex {
@SerializedName("virtual")
private final boolean virtual;
@SerializedName("objects")
private val objectsImpl: MutableMap<String, AssetObject> = HashMap(objects)
}
private final Map<String, AssetObject> objects;
public AssetIndex() {
this(false, Collections.EMPTY_MAP);
}
public AssetIndex(boolean virtual, Map<String, AssetObject> objects) {
this.virtual = virtual;
this.objects = new HashMap<>(objects);
}
public boolean isVirtual() {
return virtual;
}
public Map<String, AssetObject> getObjects() {
return Collections.unmodifiableMap(objects);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public class AssetIndexInfo extends IdDownloadInfo {
private final long totalSize;
public AssetIndexInfo() {
this("", "");
}
public AssetIndexInfo(String id, String url) {
this(id, url, null);
}
public AssetIndexInfo(String id, String url, String sha1) {
this(id, url, sha1, 0);
}
public AssetIndexInfo(String id, String url, String sha1, int size) {
this(id, url, sha1, size, 0);
}
public AssetIndexInfo(String id, String url, String sha1, int size, long totalSize) {
super(id, url, sha1, size);
this.totalSize = totalSize;
}
public long getTotalSize() {
return totalSize;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
public final class AssetObject implements Validation {
private final String hash;
private final long size;
public AssetObject() {
this("", 0);
}
public AssetObject(String hash, long size) {
this.hash = hash;
this.size = size;
}
public String getHash() {
return hash;
}
public long getSize() {
return size;
}
public String getLocation() {
return hash.substring(0, 2) + "/" + hash;
}
@Override
public void validate() throws JsonParseException {
if (StringUtils.isBlank(hash))
throw new IllegalStateException("AssetObject hash cannot be blank.");
}
}

View File

@@ -15,14 +15,24 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game
package org.jackhuang.hmcl.game;
/**
* What's circle dependency?
* When C inherits from B, and B inherits from something else, and finally inherits from C again.
*
* @author huangyuhui
*/
class CircleDependencyException : GameException {
constructor() : super() {}
constructor(message: String) : super(message) {}
constructor(message: String, cause: Throwable) : super(message, cause) {}
}
public final class CircleDependencyException extends GameException {
public CircleDependencyException() {
}
public CircleDependencyException(String message) {
super(message);
}
public CircleDependencyException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -15,8 +15,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.mod
package org.jackhuang.hmcl.game;
object ModpackManager {
/**
*
* @author huang
*/
public class ClassicLibrary {
}
}

View File

@@ -0,0 +1,54 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.io.File;
import java.util.Arrays;
import java.util.Date;
/**
* The Minecraft version for 1.5.x and earlier.
*
* @author huangyuhui
*/
public class ClassicVersion extends Version {
public ClassicVersion() {
super("Classic", "${auth_player_name} ${auth_session} --workDir ${game_directory}",
null, "net.minecraft.client.Minecraft", null, null, null, null,
Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")),
null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0);
}
private static class ClassicLibrary extends Library {
public ClassicLibrary(String name) {
super("", "", "", null, null,
new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null),
false, null, null, null);
}
}
public static boolean hasClassicVersion(File baseDirectory) {
File bin = new File(baseDirectory, "bin");
return bin.exists()
&& new File(bin, "lwjgl.jar").exists()
&& new File(bin, "jinput.jar").exists()
&& new File(bin, "lwjgl_util.jar").exists();
}
}

View File

@@ -0,0 +1,85 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class CompatibilityRule {
private final Action action;
private final OSRestriction os;
private final Map<String, Boolean> features;
public CompatibilityRule() {
this(Action.ALLOW, null);
}
public CompatibilityRule(Action action, OSRestriction os) {
this(action, os, null);
}
public CompatibilityRule(Action action, OSRestriction os, Map<String, Boolean> features) {
this.action = action;
this.os = os;
this.features = features;
}
public Action getAppliedAction(Map<String, Boolean> supportedFeatures) {
if (os != null && !os.allow())
return null;
if (features != null)
for (Map.Entry<String, Boolean> entry : features.entrySet())
if (!Objects.equals(supportedFeatures.get(entry.getKey()), entry.getValue()))
return null;
return action;
}
public static boolean appliesToCurrentEnvironment(Collection<CompatibilityRule> rules) {
return appliesToCurrentEnvironment(rules, Collections.EMPTY_MAP);
}
public static boolean appliesToCurrentEnvironment(Collection<CompatibilityRule> rules, Map<String, Boolean> features) {
if (rules == null)
return true;
Action action = Action.DISALLOW;
for (CompatibilityRule rule : rules) {
Action thisAction = rule.getAppliedAction(features);
if (thisAction != null)
action = thisAction;
}
return action == Action.ALLOW;
}
public enum Action {
ALLOW,
DISALLOW
}
}

View File

@@ -0,0 +1,311 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.logging.Level;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.LoadedOneVersionEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging;
/**
* An implementation of classic Minecraft game repository.
*
* @author huangyuhui
*/
public class DefaultGameRepository implements GameRepository {
private File baseDirectory;
protected final Map<String, Version> versions = new TreeMap<>();
protected boolean loaded = false;
public DefaultGameRepository(File baseDirectory) {
this.baseDirectory = baseDirectory;
}
public File getBaseDirectory() {
return baseDirectory;
}
public void setBaseDirectory(File baseDirectory) {
this.baseDirectory = baseDirectory;
}
@Override
public boolean hasVersion(String id) {
return versions.containsKey(id);
}
@Override
public Version getVersion(String id) {
if (!hasVersion(id))
throw new VersionNotFoundException("Version '" + id + "' does not exist.");
return versions.get(id);
}
@Override
public int getVersionCount() {
return versions.size();
}
@Override
public Collection<Version> getVersions() {
return versions.values();
}
@Override
public File getLibraryFile(Version version, Library lib) {
return new File(getBaseDirectory(), "libraries/" + lib.getPath());
}
@Override
public File getRunDirectory(String id) {
return getBaseDirectory();
}
@Override
public File getVersionJar(Version version) {
Version v = version.resolve(this);
String id = Lang.nonNull(v.getJar(), v.getId());
return new File(getVersionRoot(id), id + ".jar");
}
@Override
public File getNativeDirectory(String id) {
return new File(getVersionRoot(id), id + "-natives");
}
@Override
public File getVersionRoot(String id) {
return new File(getBaseDirectory(), "versions/" + id);
}
public File getVersionJson(String id) {
return new File(getVersionRoot(id), id + ".json");
}
public Version readVersionJson(String id) throws IOException, JsonSyntaxException {
return readVersionJson(getVersionJson(id));
}
public Version readVersionJson(File file) throws IOException, JsonSyntaxException {
return Constants.GSON.fromJson(FileUtils.readText(file), Version.class);
}
@Override
public boolean renameVersion(String from, String to) {
try {
Version fromVersion = getVersion(from);
File fromDir = getVersionRoot(from);
File toDir = getVersionRoot(to);
if (!fromDir.renameTo(toDir))
return false;
File toJson = new File(toDir, to + ".json");
File toJar = new File(toDir, to + ".jar");
if (!new File(toDir, from + ".json").renameTo(toJson)
|| !new File(toDir, from + ".jar").renameTo(toJar)) {
// recovery
toJson.renameTo(new File(toDir, from + ".json"));
toJar.renameTo(new File(toDir, from + ".jar"));
toDir.renameTo(fromDir);
return false;
}
FileUtils.writeText(toJson, Constants.GSON.toJson(fromVersion.setId(to)));
return true;
} catch (IOException | JsonSyntaxException | VersionNotFoundException e) {
return false;
}
}
public boolean removeVersionFromDisk(String id) {
File file = getVersionRoot(id);
if (!file.exists())
return true;
versions.remove(id);
return FileUtils.deleteDirectoryQuietly(file);
}
protected void refreshVersionsImpl() {
versions.clear();
if (ClassicVersion.hasClassicVersion(getBaseDirectory())) {
Version version = new ClassicVersion();
versions.put(version.getId(), version);
}
File[] files = new File(getBaseDirectory(), "versions").listFiles();
if (files != null)
for (File dir : files)
if (dir.isDirectory()) {
String id = dir.getName();
File json = new File(dir, id + ".json");
// If user renamed the json file by mistake or created the json file in a wrong name,
// we will find the only json and rename it to correct name.
if (!json.exists()) {
List<File> jsons = FileUtils.listFilesByExtension(dir, "json");
if (jsons.size() == 1)
jsons.get(0).renameTo(json);
}
Version version;
try {
version = Objects.requireNonNull(readVersionJson(json));
} catch (Exception e) {
// JsonSyntaxException or IOException or NullPointerException(!!)
// TODO: auto making up for the missing json
// TODO: and even asking for removing the redundant version folder.
continue;
}
if (!id.equals(version.getId())) {
version = version.setId(id);
try {
FileUtils.writeText(json, Constants.GSON.toJson(version));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Ignoring version {0} because wrong id {1} is set and cannot correct it.", new Object[] { id, version.getId() });
continue;
}
}
versions.put(id, version);
EventBus.EVENT_BUS.fireEvent(new LoadedOneVersionEvent(this, id));
}
loaded = true;
}
@Override
public final void refreshVersions() {
EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this));
refreshVersionsImpl();
EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this));
}
@Override
public AssetIndex getAssetIndex(String version, String assetId) throws IOException {
try {
return Objects.requireNonNull(Constants.GSON.fromJson(FileUtils.readText(getIndexFile(version, assetId)), AssetIndex.class));
} catch (JsonParseException | NullPointerException e) {
throw new IOException("Asset index file malformed", e);
}
}
@Override
public File getActualAssetDirectory(String version, String assetId) {
try {
return reconstructAssets(version, assetId);
} catch (IOException | JsonParseException e) {
Logging.LOG.log(Level.SEVERE, "Unable to reconstruct asset directory", e);
return getAssetDirectory(version, assetId);
}
}
@Override
public File getAssetDirectory(String version, String assetId) {
return new File(getBaseDirectory(), "assets");
}
@Override
public File getAssetObject(String version, String assetId, String name) throws IOException {
try {
return getAssetObject(version, assetId, getAssetIndex(version, assetId).getObjects().get(name));
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException("Unrecognized asset object " + name + " in asset " + assetId + " of version " + version, e);
}
}
@Override
public File getAssetObject(String version, String assetId, AssetObject obj) {
return getAssetObject(version, getAssetDirectory(version, assetId), obj);
}
public File getAssetObject(String version, File assetDir, AssetObject obj) {
return new File(assetDir, "objects/" + obj.getLocation());
}
@Override
public File getIndexFile(String version, String assetId) {
return new File(getAssetDirectory(version, assetId), "indexes/" + assetId + ".json");
}
@Override
public File getLoggingObject(String version, String assetId, LoggingInfo loggingInfo) {
return new File(getAssetDirectory(version, assetId), "log_configs/" + loggingInfo.getFile().getId());
}
protected File reconstructAssets(String version, String assetId) throws IOException, JsonParseException {
File assetsDir = getAssetDirectory(version, assetId);
File indexFile = getIndexFile(version, assetId);
File virtualRoot = new File(new File(assetsDir, "virtual"), assetId);
if (!indexFile.isFile())
return assetsDir;
String assetIndexContent = FileUtils.readText(indexFile);
AssetIndex index = (AssetIndex) Constants.GSON.fromJson(assetIndexContent, AssetIndex.class);
if (index == null)
return assetsDir;
if (index.isVirtual()) {
int cnt = 0;
int tot = index.getObjects().entrySet().size();
for (Map.Entry<String, AssetObject> entry : index.getObjects().entrySet()) {
File target = new File(virtualRoot, entry.getKey());
File original = getAssetObject(version, assetsDir, entry.getValue());
if (original.exists()) {
cnt++;
if (!target.isFile())
FileUtils.copyFile(original, target);
}
}
// If the scale new format existent file is lower then 0.1, use the old format.
if (cnt * 10 < tot)
return assetsDir;
else
return virtualRoot;
}
return assetsDir;
}
public boolean isLoaded() {
return loaded;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
@Immutable
public class DownloadInfo implements Validation {
@SerializedName("url")
private final String url;
@SerializedName("sha1")
private final String sha1;
@SerializedName("size")
private final int size;
public DownloadInfo() {
this("");
}
public DownloadInfo(String url) {
this(url, null);
}
public DownloadInfo(String url, String sha1) {
this(url, sha1, 0);
}
public DownloadInfo(String url, String sha1, int size) {
this.url = url;
this.sha1 = sha1;
this.size = size;
}
public String getUrl() {
return url;
}
public String getSha1() {
return sha1;
}
public int getSize() {
return size;
}
@Override
public void validate() throws JsonParseException {
if (StringUtils.isBlank(url))
throw new JsonParseException("DownloadInfo url can not be null");
}
}

View File

@@ -15,6 +15,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.auth.yggdrasil
package org.jackhuang.hmcl.game;
data class Property(val name: String, val value: String)
/**
*
* @author huangyuhui
*/
public enum DownloadType {
CLIENT,
SERVER,
WINDOWS_SERVER
}

View File

@@ -0,0 +1,50 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author huangyuhui
*/
public final class ExtractRules {
public static final ExtractRules EMPTY = new ExtractRules();
private final List<String> exclude;
public ExtractRules() {
this.exclude = Collections.EMPTY_LIST;
}
public ExtractRules(List<String> exclude) {
this.exclude = new LinkedList<>(exclude);
}
public List<String> getExclude() {
return Collections.unmodifiableList(exclude);
}
public boolean shouldExtract(String path) {
return exclude.stream().noneMatch(it -> path.startsWith(it));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
/**
*
* @author huangyuhui
*/
public class GameException extends Exception {
public GameException() {
}
public GameException(String message) {
super(message);
}
public GameException(String message, Throwable cause) {
super(message, cause);
}
public GameException(Throwable cause) {
super(cause);
}
}

View File

@@ -15,42 +15,50 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game
package org.jackhuang.hmcl.game;
import java.io.File
import java.io.IOException
import java.io.File;
import java.io.IOException;
import java.util.Collection;
/**
* Supports operations on versioning.
*
* Note that game repository will not do any tasks that connect to Internet, if do, see [org.jackhuang.hmcl.download.DependencyManager]
* Note that game repository will not do any tasks that connect to Internet, if do, see {@link org.jackhuang.hmcl.download.DependencyManager}
*
* @author huangyuhui
*/
interface GameRepository : VersionProvider {
public interface GameRepository extends VersionProvider {
/**
* Does the version of id exist?
*
* @param id the id of version
* @return true if the version exists
*/
override fun hasVersion(id: String): Boolean
@Override
boolean hasVersion(String id);
/**
* Get the version
*
* @param id the id of version
* @return the version you want
*/
override fun getVersion(id: String): Version
@Override
Version getVersion(String id);
/**
* How many version are there?
*/
fun getVersionCount(): Int
int getVersionCount();
/**
* Gets the collection of versions
*
* @return the collection of versions
*/
fun getVersions(): Collection<Version>
Collection<Version> getVersions();
/**
* Load version list.
@@ -59,20 +67,21 @@ interface GameRepository : VersionProvider {
* A time-costly operation.
* You'd better execute this method in a new thread.
*/
fun refreshVersions()
void refreshVersions();
/**
* Gets the root folder of specific version.
* The root folders the versions must be unique.
* For example, .minecraft/versions/<version name>/.
*/
fun getVersionRoot(id: String): File
File getVersionRoot(String id);
/**
* Gets the current running directory of the given version for game.
*
* @param id the version id
*/
fun getRunDirectory(id: String): File
File getRunDirectory(String id);
/**
* Get the library file in disk.
@@ -82,7 +91,7 @@ interface GameRepository : VersionProvider {
* @param lib the library, [Version.libraries]
* @return the library file
*/
fun getLibraryFile(version: Version, lib: Library): File
File getLibraryFile(Version version, Library lib);
/**
* Get the directory that native libraries will be unzipped to.
@@ -94,7 +103,7 @@ interface GameRepository : VersionProvider {
* @param id version id
* @return the native directory
*/
fun getNativeDirectory(id: String): File
File getNativeDirectory(String id);
/**
* Get minecraft jar
@@ -102,7 +111,7 @@ interface GameRepository : VersionProvider {
* @param version resolvedVersion
* @return the minecraft jar
*/
fun getVersionJar(version: Version): File
File getVersionJar(Version version);
/**
* Get minecraft jar
@@ -110,7 +119,9 @@ interface GameRepository : VersionProvider {
* @param version version id
* @return the minecraft jar
*/
fun getVersionJar(version: String): File = getVersionJar(getVersion(version).resolve(this))
default File getVersionJar(String version) {
return getVersionJar(getVersion(version).resolve(this));
}
/**
* Rename given version to new name.
@@ -121,7 +132,7 @@ interface GameRepository : VersionProvider {
* @throws java.io.IOException if I/O operation fails.
* @return true if the operation is done successfully.
*/
fun renameVersion(from: String, to: String): Boolean
boolean renameVersion(String from, String to);
/**
* Get actual asset directory.
@@ -133,7 +144,7 @@ interface GameRepository : VersionProvider {
* @throws java.io.IOException if I/O operation fails.
* @return the actual asset directory
*/
fun getActualAssetDirectory(version: String, assetId: String): File
File getActualAssetDirectory(String version, String assetId);
/**
* Get the asset directory according to the asset id.
@@ -142,7 +153,7 @@ interface GameRepository : VersionProvider {
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
* @return the asset directory
*/
fun getAssetDirectory(version: String, assetId: String): File
File getAssetDirectory(String version, String assetId);
/**
* Get the file that given asset object refers to
@@ -153,8 +164,7 @@ interface GameRepository : VersionProvider {
* @throws java.io.IOException if I/O operation fails.
* @return the file that given asset object refers to
*/
@Throws(IOException::class)
fun getAssetObject(version: String, assetId: String, name: String): File
File getAssetObject(String version, String assetId, String name) throws IOException;
/**
* Get the file that given asset object refers to
@@ -164,7 +174,7 @@ interface GameRepository : VersionProvider {
* @param obj the asset object, you can find it in [AssetIndex.objects]
* @return the file that given asset object refers to
*/
fun getAssetObject(version: String, assetId: String, obj: AssetObject): File
File getAssetObject(String version, String assetId, AssetObject obj);
/**
* Get asset index that assetId represents
@@ -173,7 +183,7 @@ interface GameRepository : VersionProvider {
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
* @return the asset index
*/
fun getAssetIndex(version: String, assetId: String): AssetIndex
AssetIndex getAssetIndex(String version, String assetId) throws IOException;
/**
* Get the asset_index.json which includes asset objects information.
@@ -181,7 +191,7 @@ interface GameRepository : VersionProvider {
* @param version the id of specific version that is relevant to [assetId]
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
*/
fun getIndexFile(version: String, assetId: String): File
File getIndexFile(String version, String assetId);
/**
* Get logging object
@@ -191,6 +201,6 @@ interface GameRepository : VersionProvider {
* @param loggingInfo the logging info
* @return the file that loggingInfo refers to
*/
fun getLoggingObject(version: String, assetId: String, loggingInfo: LoggingInfo): File
File getLoggingObject(String version, String assetId, LoggingInfo loggingInfo);
}
}

View File

@@ -0,0 +1,129 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.jackhuang.hmcl.util.Charsets;
import org.jackhuang.hmcl.util.IOUtils;
import java.io.File;
import java.io.IOException;
/**
* @author huangyuhui
*/
public final class GameVersion {
private static int lessThan32(byte[] b, int x) {
for (; x < b.length; x++)
if (b[x] < 32)
return x;
return -1;
}
public static int matchArray(byte[] a, byte[] b) {
for (int i = 0; i < a.length - b.length; i++) {
int j = 1;
for (int k = 0; k < b.length; k++) {
if (b[k] == a[(i + k)])
continue;
j = 0;
break;
}
if (j != 0)
return i;
}
return -1;
}
private static String getVersionOfOldMinecraft(ZipFile file, ZipArchiveEntry entry) throws IOException {
byte[] tmp = IOUtils.readFullyAsByteArray(file.getInputStream(entry));
byte[] bytes = "Minecraft Minecraft ".getBytes(Charsets.US_ASCII);
int j = matchArray(tmp, bytes);
if (j < 0)
return null;
int i = j + bytes.length;
if ((j = lessThan32(tmp, i)) < 0)
return null;
return new String(tmp, i, j - i, Charsets.US_ASCII);
}
private static String getVersionOfNewMinecraft(ZipFile file, ZipArchiveEntry entry) throws IOException {
byte[] tmp = IOUtils.readFullyAsByteArray(file.getInputStream(entry));
byte[] str = "-server.txt".getBytes(Charsets.US_ASCII);
int j = matchArray(tmp, str);
if (j < 0) return null;
int i = j + str.length;
i += 11;
j = lessThan32(tmp, i);
if (j < 0) return null;
String result = new String(tmp, i, j - i, Charsets.US_ASCII);
char ch = result.charAt(0);
// 1.8.1+
if (ch < '0' || ch > '9') {
str = "Can't keep up! Did the system time change, or is the server overloaded?".getBytes(Charsets.US_ASCII);
j = matchArray(tmp, str);
if (j < 0) return null;
i = -1;
while (j > 0) {
if (tmp[j] >= 48 && tmp[j] <= 57) {
i = j;
break;
}
j--;
}
if (i == -1) return null;
int k = i;
if (tmp[i + 1] >= (int) 'a' && tmp[i + 1] <= (int) 'z')
i++;
while (tmp[k] >= 48 && tmp[k] <= 57 || tmp[k] == (int) '-' || tmp[k] == (int) '.' || tmp[k] >= 97 && tmp[k] <= (int) 'z')
k--;
k++;
return new String(tmp, k, i - k + 1, Charsets.US_ASCII);
}
return result;
}
public static String minecraftVersion(File file) {
if (file == null || !file.exists() || !file.isFile() || !file.canRead())
return null;
ZipFile f = null;
try {
f = new ZipFile(file);
ZipArchiveEntry minecraft = f
.getEntry("net/minecraft/client/Minecraft.class");
if (minecraft != null)
return getVersionOfOldMinecraft(f, minecraft);
ZipArchiveEntry main = f.getEntry("net/minecraft/client/main/Main.class");
ZipArchiveEntry minecraftserver = f.getEntry("net/minecraft/server/MinecraftServer.class");
if ((main != null) && (minecraftserver != null))
return getVersionOfNewMinecraft(f, minecraftserver);
return null;
} catch (IOException e) {
return null;
} finally {
IOUtils.closeQuietly(f);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.StringUtils;
/**
*
* @author huangyuhui
*/
@Immutable
public class IdDownloadInfo extends DownloadInfo {
@SerializedName("id")
private final String id;
public IdDownloadInfo() {
this("", "");
}
public IdDownloadInfo(String id, String url) {
this(id, url, null);
}
public IdDownloadInfo(String id, String url, String sha1) {
this(id, url, sha1, 0);
}
public IdDownloadInfo(String id, String url, String sha1, int size) {
super(url, sha1, size);
this.id = id;
}
public String getId() {
return id;
}
@Override
public void validate() throws JsonParseException {
super.validate();
if (StringUtils.isBlank(id))
throw new JsonParseException("IdDownloadInfo id can not be null");
}
}

View File

@@ -0,0 +1,303 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.io.File;
import java.io.Serializable;
import org.jackhuang.hmcl.util.JavaVersion;
/**
*
* @author huangyuhui
*/
public class LaunchOptions implements Serializable {
private File gameDir;
private JavaVersion java;
private String versionName;
private String profileName;
private String minecraftArgs;
private String javaArgs;
private Integer minMemory;
private Integer maxMemory;
private Integer metaspace;
private Integer width;
private Integer height;
private boolean fullscreen;
private String serverIp;
private String wrapper;
private String proxyHost;
private String proxyPort;
private String proxyUser;
private String proxyPass;
private boolean noGeneratedJVMArgs;
private String precalledCommand;
/**
* The game directory
*/
public File getGameDir() {
return gameDir;
}
/**
* The Java Environment that Minecraft runs on.
*/
public JavaVersion getJava() {
return java;
}
/**
* Will shown in the left bottom corner of the main menu of Minecraft.
* null if use the id of launch version.
*/
public String getVersionName() {
return versionName;
}
/**
* Don't know what the hell this is.
*/
public String getProfileName() {
return profileName;
}
/**
* User custom additional minecraft command line arguments.
*/
public String getMinecraftArgs() {
return minecraftArgs;
}
/**
* User custom additional java virtual machine command line arguments.
*/
public String getJavaArgs() {
return javaArgs;
}
/**
* The minimum memory that the JVM can allocate.
*/
public Integer getMinMemory() {
return minMemory;
}
/**
* The maximum memory that the JVM can allocate.
*/
public Integer getMaxMemory() {
return maxMemory;
}
/**
* The maximum metaspace memory that the JVM can allocate.
* For Java 7 -XX:PermSize and Java 8 -XX:MetaspaceSize
* Containing class instances.
*/
public Integer getMetaspace() {
return metaspace;
}
/**
* The initial game window width
*/
public Integer getWidth() {
return width;
}
/**
* The initial game window height
*/
public Integer getHeight() {
return height;
}
/**
* Is inital game window fullscreen.
*/
public boolean isFullscreen() {
return fullscreen;
}
/**
* The server ip that will connect to when enter game main menu.
*/
public String getServerIp() {
return serverIp;
}
/**
* i.e. optirun
*/
public String getWrapper() {
return wrapper;
}
/**
* The host of the proxy address
*/
public String getProxyHost() {
return proxyHost;
}
/**
* the port of the proxy address.
*/
public String getProxyPort() {
return proxyPort;
}
/**
* The user name of the proxy, optional.
*/
public String getProxyUser() {
return proxyUser;
}
/**
* The password of the proxy, optional
*/
public String getProxyPass() {
return proxyPass;
}
/**
* Prevent game launcher from generating default JVM arguments like max memory.
*/
public boolean isNoGeneratedJVMArgs() {
return noGeneratedJVMArgs;
}
/**
* Called command line before launching the game.
*/
public String getPrecalledCommand() {
return precalledCommand;
}
public static class Builder {
LaunchOptions options = new LaunchOptions();
public LaunchOptions create() {
return options;
}
public Builder setGameDir(File gameDir) {
options.gameDir = gameDir;
return this;
}
public Builder setJava(JavaVersion java) {
options.java = java;
return this;
}
public Builder setVersionName(String versionName) {
options.versionName = versionName;
return this;
}
public Builder setProfileName(String profileName) {
options.profileName = profileName;
return this;
}
public Builder setMinecraftArgs(String minecraftArgs) {
options.minecraftArgs = minecraftArgs;
return this;
}
public Builder setJavaArgs(String javaArgs) {
options.javaArgs = javaArgs;
return this;
}
public Builder setMinMemory(Integer minMemory) {
options.minMemory = minMemory;
return this;
}
public Builder setMaxMemory(Integer maxMemory) {
options.maxMemory = maxMemory;
return this;
}
public Builder setMetaspace(Integer metaspace) {
options.metaspace = metaspace;
return this;
}
public Builder setWidth(Integer width) {
options.width = width;
return this;
}
public Builder setHeight(Integer height) {
options.height = height;
return this;
}
public Builder setFullscreen(boolean fullscreen) {
options.fullscreen = fullscreen;
return this;
}
public Builder setServerIp(String serverIp) {
options.serverIp = serverIp;
return this;
}
public Builder setWrapper(String wrapper) {
options.wrapper = wrapper;
return this;
}
public Builder setProxyHost(String proxyHost) {
options.proxyHost = proxyHost;
return this;
}
public Builder setProxyPort(String proxyPort) {
options.proxyPort = proxyPort;
return this;
}
public Builder setProxyUser(String proxyUser) {
options.proxyUser = proxyUser;
return this;
}
public Builder setProxyPass(String proxyPass) {
options.proxyPass = proxyPass;
return this;
}
public Builder setNoGeneratedJVMArgs(boolean noGeneratedJVMArgs) {
options.noGeneratedJVMArgs = noGeneratedJVMArgs;
return this;
}
public Builder setPrecalledCommand(String precalledCommand) {
options.precalledCommand = precalledCommand;
return this;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class LibrariesDownloadInfo {
private final LibraryDownloadInfo artifact;
private final Map<String, LibraryDownloadInfo> classifiers;
public LibrariesDownloadInfo(LibraryDownloadInfo artifact) {
this(artifact, Collections.EMPTY_MAP);
}
public LibrariesDownloadInfo(LibraryDownloadInfo artifact, Map<String, LibraryDownloadInfo> classifiers) {
this.artifact = artifact;
this.classifiers = new HashMap<>(classifiers);
}
public LibraryDownloadInfo getArtifact() {
return artifact;
}
public Map<String, LibraryDownloadInfo> getClassifiers() {
return classifiers == null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(classifiers);
}
}

View File

@@ -0,0 +1,203 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.OperatingSystem;
import org.jackhuang.hmcl.util.Platform;
/**
* A class that describes a Minecraft dependency.
*
* @author huangyuhui
*/
public class Library {
private final String groupId;
private final String artifactId;
private final String version;
private final String classifier;
private final String url;
private final LibrariesDownloadInfo downloads;
private final LibraryDownloadInfo download;
private final ExtractRules extract;
private final boolean lateload;
private final Map<OperatingSystem, String> natives;
private final List<CompatibilityRule> rules;
private final String path;
public Library(String groupId, String artifactId, String version) {
this(groupId, artifactId, version, null, null, null);
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads) {
this(groupId, artifactId, version, classifier, url, downloads, false);
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload) {
this(groupId, artifactId, version, classifier, url, downloads, lateload, null, null, null);
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
if (classifier == null)
if (natives != null && natives.containsKey(OperatingSystem.CURRENT_OS))
this.classifier = natives.get(OperatingSystem.CURRENT_OS).replace("${arch}", Platform.PLATFORM.getBit());
else
this.classifier = null;
else
this.classifier = classifier;
this.url = url;
this.downloads = downloads;
this.extract = extract;
this.lateload = lateload;
this.natives = natives;
this.rules = rules;
LibraryDownloadInfo temp = null;
if (downloads != null)
if (isNative())
temp = downloads.getClassifiers().get(this.classifier);
else
temp = downloads.getArtifact();
if (temp != null && temp.getPath() != null)
path = temp.getPath();
else
path = String.format("%s/%s/%s/%s-%s", groupId.replace(".", "/"), artifactId, version, artifactId, version)
+ (this.classifier == null ? "" : "-" + this.classifier) + ".jar";
download = new LibraryDownloadInfo(path,
Lang.nonNull(Lang.nonNull(temp != null ? temp.getUrl() : null), Lang.nonNull(url, Constants.DEFAULT_LIBRARY_URL) + path),
temp != null ? temp.getSha1() : null,
temp != null ? temp.getSize() : 0
);
}
public String getGroupId() {
return groupId;
}
public String getArtifactId() {
return artifactId;
}
public String getVersion() {
return version;
}
public ExtractRules getExtract() {
return extract == null ? ExtractRules.EMPTY : extract;
}
public boolean appliesToCurrentEnvironment() {
return CompatibilityRule.appliesToCurrentEnvironment(rules);
}
public boolean isNative() {
return natives != null && appliesToCurrentEnvironment();
}
public String getPath() {
return path;
}
public LibraryDownloadInfo getDownload() {
return download;
}
public boolean isLateload() {
return lateload;
}
@Override
public String toString() {
return "Library[" + groupId + ":" + artifactId + ":" + version + "]";
}
public static Library fromName(String name) {
return fromName(name, null, null, null, null, null);
}
public static Library fromName(String name, String url, LibrariesDownloadInfo downloads, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
String[] arr = name.split(":", 4);
if (arr.length != 3 && arr.length != 4)
throw new IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version(:classifier).");
return new Library(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, url, downloads, false, extract, natives, rules);
}
public static class Serializer implements JsonDeserializer<Library>, JsonSerializer<Library> {
public static final Serializer INSTANCE = new Serializer();
private Serializer() {
}
@Override
public Library deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
if (json == null || json == JsonNull.INSTANCE)
return null;
JsonObject jsonObject = json.getAsJsonObject();
if (!jsonObject.has("name"))
throw new JsonParseException("Library name not found.");
return fromName(
jsonObject.get("name").getAsString(),
jsonObject.has("url") ? jsonObject.get("url").getAsString() : null,
context.deserialize(jsonObject.get("downloads"), LibrariesDownloadInfo.class),
context.deserialize(jsonObject.get("extract"), ExtractRules.class),
context.deserialize(jsonObject.get("natives"), new TypeToken<Map<OperatingSystem, String>>() {
}.getType()),
context.deserialize(jsonObject.get("rules"), new TypeToken<List<CompatibilityRule>>() {
}.getType()));
}
@Override
public JsonElement serialize(Library src, Type type, JsonSerializationContext context) {
if (src == null)
return JsonNull.INSTANCE;
JsonObject obj = new JsonObject();
obj.addProperty("name", src.groupId + ":" + src.artifactId + ":" + src.version);
obj.addProperty("url", src.url);
obj.add("downloads", context.serialize(src.downloads));
obj.add("extract", context.serialize(src.extract));
obj.add("natives", context.serialize(src.natives, new TypeToken<Map<OperatingSystem, String>>() {
}.getType()));
obj.add("rules", context.serialize(src.rules, new TypeToken<List<CompatibilityRule>>() {
}.getType()));
return obj;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public class LibraryDownloadInfo extends DownloadInfo {
@SerializedName("path")
private final String path;
public LibraryDownloadInfo() {
this(null);
}
public LibraryDownloadInfo(String path) {
this(path, "");
}
public LibraryDownloadInfo(String path, String url) {
this(path, url, null);
}
public LibraryDownloadInfo(String path, String url, String sha1) {
this(path, url, sha1, 0);
}
public LibraryDownloadInfo(String path, String url, String sha1, int size) {
super(url, sha1, size);
this.path = path;
}
public String getPath() {
return path;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.Validation;
/**
*
* @author huangyuhui
*/
public final class LoggingInfo implements Validation {
@SerializedName("file")
private final IdDownloadInfo file;
@SerializedName("argument")
private final String argument;
@SerializedName("type")
private final String type;
public LoggingInfo() {
this(new IdDownloadInfo());
}
public LoggingInfo(IdDownloadInfo file) {
this(file, "");
}
public LoggingInfo(IdDownloadInfo file, String argument) {
this(file, argument, "");
}
public LoggingInfo(IdDownloadInfo file, String argument, String type) {
this.file = file;
this.argument = argument;
this.type = type;
}
public IdDownloadInfo getFile() {
return file;
}
public String getArgument() {
return argument;
}
public String getType() {
return type;
}
@Override
public void validate() throws JsonParseException {
file.validate();
if (StringUtils.isBlank(argument))
throw new JsonParseException("LoggingInfo.argument is empty.");
if (StringUtils.isBlank(type))
throw new JsonParseException("LoggingInfo.type is empty.");
}
}

View File

@@ -0,0 +1,79 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.OperatingSystem;
/**
*
* @author huangyuhui
*/
public final class OSRestriction {
private final OperatingSystem name;
private final String version;
private final String arch;
public OSRestriction() {
this(OperatingSystem.UNKNOWN);
}
public OSRestriction(OperatingSystem name) {
this(name, null);
}
public OSRestriction(OperatingSystem name, String version) {
this(name, version, null);
}
public OSRestriction(OperatingSystem name, String version, String arch) {
this.name = name;
this.version = version;
this.arch = arch;
}
public OperatingSystem getName() {
return name;
}
public String getVersion() {
return version;
}
public String getArch() {
return arch;
}
public boolean allow() {
if (name != OperatingSystem.UNKNOWN && name != OperatingSystem.CURRENT_OS)
return false;
if (version != null)
if (Lang.test(() -> !Pattern.compile(version).matcher(OperatingSystem.SYSTEM_VERSION).matches()))
return false;
if (arch != null)
if (Lang.test(() -> !Pattern.compile(arch).matcher(OperatingSystem.SYSTEM_ARCHITECTURE).matches()))
return false;
return true;
}
}

View File

@@ -15,21 +15,28 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game
package org.jackhuang.hmcl.game;
import com.google.gson.annotations.SerializedName
enum class ReleaseType(val id: String) {
@SerializedName("release")
/**
*
* @author huangyuhui
*/
public enum ReleaseType {
RELEASE("release"),
@SerializedName("snapshot")
SNAPSHOT("snapshot"),
@SerializedName("modified")
MODIFIED("modified"),
@SerializedName("old-beta")
OLD_BETA("old-beta"),
@SerializedName("old-alpha")
OLD_ALPHA("old-alpha"),
@SerializedName("unknown")
UNKNOWN("unknown")
}
UNKNOWN("unknown");
private final String id;
private ReleaseType(String id) {
this.id = id;
}
public String getId() {
return id;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public class RuledArgument implements Argument {
private final List<CompatibilityRule> rules;
private final List<String> value;
public RuledArgument() {
this(null, null);
}
public RuledArgument(List<CompatibilityRule> rules, List<String> args) {
this.rules = rules;
this.value = args;
}
public List<CompatibilityRule> getRules() {
return Collections.unmodifiableList(rules);
}
public List<String> getValue() {
return Collections.unmodifiableList(value);
}
@Override
public Object clone() {
return new RuledArgument(
rules == null ? null : new ArrayList<>(rules),
value == null ? null : new ArrayList<>(value)
);
}
@Override
public List<String> toString(Map<String, String> keys, Map<String, Boolean> features) {
if (CompatibilityRule.appliesToCurrentEnvironment(rules) && value != null)
return value.stream()
.map(StringArgument::new)
.map(str -> str.toString(keys, features).get(0))
.collect(Collectors.toList());
return Collections.EMPTY_LIST;
}
public static class Serializer implements JsonSerializer<RuledArgument>, JsonDeserializer<RuledArgument> {
public static final Serializer INSTANCE = new Serializer();
private Serializer() {
}
@Override
public JsonElement serialize(RuledArgument src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.add("rules", context.serialize(src.rules));
obj.add("value", context.serialize(src.value));
return obj;
}
@Override
public RuledArgument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
RuledArgument a = new RuledArgument(
context.deserialize(obj.get("rules"), new TypeToken<List<CompatibilityRule>>() {
}.getType()),
obj.get("value").isJsonPrimitive()
? Collections.singletonList(obj.get("value").getAsString())
: context.deserialize(obj.get("value"), new TypeToken<List<String>>() {
}.getType()));
return a;
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public class SimpleVersionProvider implements VersionProvider {
protected final Map<String, Version> versionMap = new HashMap<>();
@Override
public boolean hasVersion(String id) {
return versionMap.containsKey(id);
}
@Override
public Version getVersion(String id) {
if (!hasVersion(id))
throw new VersionNotFoundException("Version id " + id + " not found");
else
return versionMap.get(id);
}
public void addVersion(Version version) {
versionMap.put(version.getId(), version);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.game;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.util.Immutable;
/**
*
* @author huangyuhui
*/
@Immutable
public final class StringArgument implements Argument {
private final String argument;
public StringArgument(String argument) {
this.argument = argument;
}
public String getArgument() {
return argument;
}
@Override
public List<String> toString(Map<String, String> keys, Map<String, Boolean> features) {
String res = argument;
Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");
Matcher m = pattern.matcher(argument);
while (m.find()) {
String entry = m.group();
res = res.replace(entry, keys.getOrDefault(entry, entry));
}
return Collections.singletonList(res);
}
}

Some files were not shown because too many files have changed in this diff Show More