commit
c4882e7162
@ -582,6 +582,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||||
|
|
||||||
|
// Legacy settings
|
||||||
|
m_settings->registerSetting("OnlineFixes", false);
|
||||||
|
|
||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||||
m_settings->registerSetting("CustomOpenALPath", "");
|
m_settings->registerSetting("CustomOpenALPath", "");
|
||||||
|
@ -45,10 +45,12 @@ QString JavaVersion::toString() const
|
|||||||
|
|
||||||
bool JavaVersion::requiresPermGen()
|
bool JavaVersion::requiresPermGen()
|
||||||
{
|
{
|
||||||
if (m_parseable) {
|
return !m_parseable || m_major < 8;
|
||||||
return m_major < 8;
|
}
|
||||||
}
|
|
||||||
return true;
|
bool JavaVersion::isModular()
|
||||||
|
{
|
||||||
|
return m_parseable && m_major >= 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaVersion::operator<(const JavaVersion& rhs)
|
bool JavaVersion::operator<(const JavaVersion& rhs)
|
||||||
|
@ -25,6 +25,8 @@ class JavaVersion {
|
|||||||
|
|
||||||
bool requiresPermGen();
|
bool requiresPermGen();
|
||||||
|
|
||||||
|
bool isModular();
|
||||||
|
|
||||||
QString toString() const;
|
QString toString() const;
|
||||||
|
|
||||||
int major() { return m_major; }
|
int major() { return m_major; }
|
||||||
|
@ -184,6 +184,10 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||||
|
|
||||||
|
// Legacy-related options
|
||||||
|
auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false);
|
||||||
|
m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings);
|
||||||
|
|
||||||
m_settings->set("InstanceType", "OneSix");
|
m_settings->set("InstanceType", "OneSix");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,20 +517,28 @@ QStringList MinecraftInstance::javaArguments()
|
|||||||
|
|
||||||
args << "-Duser.language=en";
|
args << "-Duser.language=en";
|
||||||
|
|
||||||
|
if (javaVersion.isModular() && shouldApplyOnlineFixes())
|
||||||
|
// allow reflective access to java.net - required by the skin fix
|
||||||
|
args << "--add-opens"
|
||||||
|
<< "java.base/java.net=ALL-UNNAMED";
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MinecraftInstance::getLauncher()
|
QString MinecraftInstance::getLauncher()
|
||||||
{
|
{
|
||||||
auto profile = m_components->getProfile();
|
|
||||||
|
|
||||||
// use legacy launcher if the traits are set
|
// use legacy launcher if the traits are set
|
||||||
if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch"))
|
if (traits().contains("legacyLaunch") || traits().contains("alphaLaunch"))
|
||||||
return "legacy";
|
return "legacy";
|
||||||
|
|
||||||
return "standard";
|
return "standard";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MinecraftInstance::shouldApplyOnlineFixes()
|
||||||
|
{
|
||||||
|
return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
QMap<QString, QString> MinecraftInstance::getVariables()
|
QMap<QString, QString> MinecraftInstance::getVariables()
|
||||||
{
|
{
|
||||||
QMap<QString, QString> out;
|
QMap<QString, QString> out;
|
||||||
@ -716,6 +728,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
|||||||
launchScript += "traits " + trait + "\n";
|
launchScript += "traits " + trait + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldApplyOnlineFixes())
|
||||||
|
launchScript += "onlineFixes true\n";
|
||||||
|
|
||||||
launchScript += "launcher " + getLauncher() + "\n";
|
launchScript += "launcher " + getLauncher() + "\n";
|
||||||
|
|
||||||
// qDebug() << "Generated launch script:" << launchScript;
|
// qDebug() << "Generated launch script:" << launchScript;
|
||||||
|
@ -129,6 +129,7 @@ class MinecraftInstance : public BaseInstance {
|
|||||||
/// get arguments passed to java
|
/// get arguments passed to java
|
||||||
QStringList javaArguments();
|
QStringList javaArguments();
|
||||||
QString getLauncher();
|
QString getLauncher();
|
||||||
|
bool shouldApplyOnlineFixes();
|
||||||
|
|
||||||
/// get variables for launch command variable substitution/environment
|
/// get variables for launch command variable substitution/environment
|
||||||
QMap<QString, QString> getVariables() override;
|
QMap<QString, QString> getVariables() override;
|
||||||
|
@ -105,6 +105,17 @@ void LauncherPartLaunch::executeTask()
|
|||||||
auto instance = m_parent->instance();
|
auto instance = m_parent->instance();
|
||||||
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
|
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
|
||||||
|
|
||||||
|
QString legacyJarPath;
|
||||||
|
if (minecraftInstance->getLauncher() == "legacy" || minecraftInstance->shouldApplyOnlineFixes()) {
|
||||||
|
legacyJarPath = APPLICATION->getJarPath("NewLaunchLegacy.jar");
|
||||||
|
if (legacyJarPath.isEmpty()) {
|
||||||
|
const char* reason = QT_TR_NOOP("Legacy launcher library could not be found. Please check your installation.");
|
||||||
|
emit logLine(tr(reason), MessageLevel::Fatal);
|
||||||
|
emitFailed(tr(reason));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
|
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
|
||||||
QStringList args = minecraftInstance->javaArguments();
|
QStringList args = minecraftInstance->javaArguments();
|
||||||
QString allArgs = args.join(", ");
|
QString allArgs = args.join(", ");
|
||||||
@ -120,6 +131,9 @@ void LauncherPartLaunch::executeTask()
|
|||||||
auto classPath = minecraftInstance->getClassPath();
|
auto classPath = minecraftInstance->getClassPath();
|
||||||
classPath.prepend(jarPath);
|
classPath.prepend(jarPath);
|
||||||
|
|
||||||
|
if (!legacyJarPath.isEmpty())
|
||||||
|
classPath.prepend(legacyJarPath);
|
||||||
|
|
||||||
auto natPath = minecraftInstance->getNativePath();
|
auto natPath = minecraftInstance->getNativePath();
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
if (!fitsInLocal8bit(natPath)) {
|
if (!fitsInLocal8bit(natPath)) {
|
||||||
|
@ -119,6 +119,9 @@ void MinecraftPage::applySettings()
|
|||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
|
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
|
||||||
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
|
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
|
||||||
|
|
||||||
|
// Legacy settings
|
||||||
|
s->set("OnlineFixes", ui->onlineFixes->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftPage::loadSettings()
|
void MinecraftPage::loadSettings()
|
||||||
@ -170,6 +173,8 @@ void MinecraftPage::loadSettings()
|
|||||||
|
|
||||||
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
|
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
|
||||||
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
|
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
|
||||||
|
|
||||||
|
ui->onlineFixes->setChecked(s->get("OnlineFixes").toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftPage::retranslate()
|
void MinecraftPage::retranslate()
|
||||||
|
@ -138,7 +138,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="showGameTimeWithoutDays">
|
<widget class="QCheckBox" name="showGameTimeWithoutDays">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show time spent playing in hours</string>
|
<string>Show time spent playing in hours</string>
|
||||||
@ -197,6 +197,25 @@
|
|||||||
<string>Tweaks</string>
|
<string>Tweaks</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="legacySettingsGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Legacy settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="onlineFixes">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable online fixes (experimental)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
|
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -253,6 +254,14 @@ void InstanceSettingsPage::applySettings()
|
|||||||
m_settings->reset("InstanceAccountId");
|
m_settings->reset("InstanceAccountId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool overrideLegacySettings = ui->legacySettingsGroupBox->isChecked();
|
||||||
|
m_settings->set("OverrideLegacySettings", overrideLegacySettings);
|
||||||
|
if (overrideLegacySettings) {
|
||||||
|
m_settings->set("OnlineFixes", ui->onlineFixes->isChecked());
|
||||||
|
} else {
|
||||||
|
m_settings->reset("OnlineFixes");
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This should probably be called by a signal instead
|
// FIXME: This should probably be called by a signal instead
|
||||||
m_instance->updateRuntimeContext();
|
m_instance->updateRuntimeContext();
|
||||||
}
|
}
|
||||||
@ -356,6 +365,9 @@ void InstanceSettingsPage::loadSettings()
|
|||||||
|
|
||||||
ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool());
|
ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool());
|
||||||
updateAccountsMenu();
|
updateAccountsMenu();
|
||||||
|
|
||||||
|
ui->legacySettingsGroupBox->setChecked(m_settings->get("OverrideLegacySettings").toBool());
|
||||||
|
ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceSettingsPage::on_javaDetectBtn_clicked()
|
void InstanceSettingsPage::on_javaDetectBtn_clicked()
|
||||||
|
@ -583,6 +583,31 @@
|
|||||||
<string>Miscellaneous</string>
|
<string>Miscellaneous</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="legacySettingsGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Legacy settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="onlineFixes">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable online fixes (experimental)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="gameTimeGroupBox">
|
<widget class="QGroupBox" name="gameTimeGroupBox">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
@ -11,15 +11,33 @@ set(SRC
|
|||||||
org/prismlauncher/launcher/Launcher.java
|
org/prismlauncher/launcher/Launcher.java
|
||||||
org/prismlauncher/launcher/impl/AbstractLauncher.java
|
org/prismlauncher/launcher/impl/AbstractLauncher.java
|
||||||
org/prismlauncher/launcher/impl/StandardLauncher.java
|
org/prismlauncher/launcher/impl/StandardLauncher.java
|
||||||
org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java
|
|
||||||
org/prismlauncher/launcher/impl/legacy/LegacyFrame.java
|
|
||||||
org/prismlauncher/exception/ParameterNotFoundException.java
|
org/prismlauncher/exception/ParameterNotFoundException.java
|
||||||
org/prismlauncher/exception/ParseException.java
|
org/prismlauncher/exception/ParseException.java
|
||||||
org/prismlauncher/utils/Parameters.java
|
org/prismlauncher/utils/Parameters.java
|
||||||
org/prismlauncher/utils/ReflectionUtils.java
|
org/prismlauncher/utils/ReflectionUtils.java
|
||||||
org/prismlauncher/utils/logging/Level.java
|
org/prismlauncher/utils/logging/Level.java
|
||||||
org/prismlauncher/utils/logging/Log.java
|
org/prismlauncher/utils/logging/Log.java
|
||||||
net/minecraft/Launcher.java
|
org/prismlauncher/legacy/LegacyProxy.java
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(LEGACY_SRC
|
||||||
|
legacy/org/prismlauncher/legacy/LegacyFrame.java
|
||||||
|
legacy/org/prismlauncher/legacy/LegacyLauncher.java
|
||||||
|
legacy/org/prismlauncher/legacy/fix/online/Handler.java
|
||||||
|
legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java
|
||||||
|
legacy/org/prismlauncher/legacy/fix/online/SkinFix.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/Base64.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/api/MojangApi.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/api/Texture.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/json/JsonParser.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java
|
||||||
|
legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java
|
||||||
|
legacy/net/minecraft/Launcher.java
|
||||||
|
legacy/org/prismlauncher/legacy/LegacyProxy.java
|
||||||
|
)
|
||||||
|
|
||||||
add_jar(NewLaunch ${SRC})
|
add_jar(NewLaunch ${SRC})
|
||||||
|
add_jar(NewLaunchLegacy ${LEGACY_SRC} INCLUDE_JARS NewLaunch)
|
||||||
install_jar(NewLaunch "${JARS_DEST_DIR}")
|
install_jar(NewLaunch "${JARS_DEST_DIR}")
|
||||||
|
install_jar(NewLaunchLegacy "${JARS_DEST_DIR}")
|
||||||
|
@ -92,12 +92,11 @@ public final class Launcher extends Applet implements AppletStub {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (documentBase == null) {
|
if (documentBase == null) {
|
||||||
if (applet.getClass().getPackage().getName().startsWith("com.mojang.")) {
|
if (applet.getClass().getPackage().getName().startsWith("com.mojang"))
|
||||||
// Special case only for Classic versions
|
// Special case only for Classic versions
|
||||||
documentBase = new URL("http://www.minecraft.net:80/game/");
|
documentBase = new URL("http://www.minecraft.net:80/game/");
|
||||||
} else {
|
else
|
||||||
documentBase = new URL("http://www.minecraft.net/game/");
|
documentBase = new URL("http://www.minecraft.net/game/");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
@ -52,7 +52,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.prismlauncher.launcher.impl.legacy;
|
package org.prismlauncher.legacy;
|
||||||
|
|
||||||
import org.prismlauncher.utils.logging.Log;
|
import org.prismlauncher.utils.logging.Log;
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ import javax.swing.JFrame;
|
|||||||
|
|
||||||
import net.minecraft.Launcher;
|
import net.minecraft.Launcher;
|
||||||
|
|
||||||
public final class LegacyFrame extends JFrame {
|
final class LegacyFrame extends JFrame {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final Launcher launcher;
|
private final Launcher launcher;
|
||||||
@ -130,7 +130,7 @@ public final class LegacyFrame extends JFrame {
|
|||||||
|
|
||||||
launcher.setParameter("username", user);
|
launcher.setParameter("username", user);
|
||||||
launcher.setParameter("sessionid", session);
|
launcher.setParameter("sessionid", session);
|
||||||
launcher.setParameter("stand-alone", true); // Show the quit button. TODO: why won't this work?
|
launcher.setParameter("stand-alone", true); // Show the quit button. This often doesn't seem to work.
|
||||||
launcher.setParameter("haspaid", true); // Some old versions need this for world saves to work.
|
launcher.setParameter("haspaid", true); // Some old versions need this for world saves to work.
|
||||||
launcher.setParameter("demo", demo);
|
launcher.setParameter("demo", demo);
|
||||||
launcher.setParameter("fullscreen", false);
|
launcher.setParameter("fullscreen", false);
|
@ -53,23 +53,27 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.prismlauncher.launcher.impl.legacy;
|
package org.prismlauncher.legacy;
|
||||||
|
|
||||||
import org.prismlauncher.launcher.impl.AbstractLauncher;
|
import org.prismlauncher.launcher.impl.AbstractLauncher;
|
||||||
import org.prismlauncher.utils.Parameters;
|
import org.prismlauncher.utils.Parameters;
|
||||||
import org.prismlauncher.utils.ReflectionUtils;
|
import org.prismlauncher.utils.ReflectionUtils;
|
||||||
import org.prismlauncher.utils.logging.Log;
|
import org.prismlauncher.utils.logging.Log;
|
||||||
|
|
||||||
|
import java.applet.Applet;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to launch old versions that support applets.
|
* Used to launch old versions which support applets.
|
||||||
*/
|
*/
|
||||||
public final class LegacyLauncher extends AbstractLauncher {
|
final class LegacyLauncher extends AbstractLauncher {
|
||||||
private final String user, session;
|
private final String user, session;
|
||||||
private final String title;
|
private final String title;
|
||||||
private final String appletClass;
|
private final String appletClass;
|
||||||
@ -93,11 +97,9 @@ public final class LegacyLauncher extends AbstractLauncher {
|
|||||||
@Override
|
@Override
|
||||||
public void launch() throws Throwable {
|
public void launch() throws Throwable {
|
||||||
Class<?> main = ClassLoader.getSystemClassLoader().loadClass(mainClassName);
|
Class<?> main = ClassLoader.getSystemClassLoader().loadClass(mainClassName);
|
||||||
Field gameDirField = ReflectionUtils.findMinecraftGameDirField(main);
|
Field gameDirField = findMinecraftGameDirField(main);
|
||||||
|
|
||||||
if (gameDirField == null)
|
if (gameDirField != null) {
|
||||||
Log.warning("Could not find Minecraft folder field");
|
|
||||||
else {
|
|
||||||
gameDirField.setAccessible(true);
|
gameDirField.setAccessible(true);
|
||||||
gameDirField.set(null, new File(gameDir));
|
gameDirField.set(null, new File(gameDir));
|
||||||
}
|
}
|
||||||
@ -106,7 +108,7 @@ public final class LegacyLauncher extends AbstractLauncher {
|
|||||||
System.setProperty("minecraft.applet.TargetDirectory", gameDir);
|
System.setProperty("minecraft.applet.TargetDirectory", gameDir);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(appletClass));
|
LegacyFrame window = new LegacyFrame(title, createAppletClass(appletClass));
|
||||||
|
|
||||||
window.start(user, session, width, height, maximize, serverAddress, serverPort, gameArgs.contains("--demo"));
|
window.start(user, session, width, height, maximize, serverAddress, serverPort, gameArgs.contains("--demo"));
|
||||||
return;
|
return;
|
||||||
@ -115,9 +117,39 @@ public final class LegacyLauncher extends AbstractLauncher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find and invoke the main method, this time without size parameters
|
// find and invoke the main method, this time without size parameters - in all
|
||||||
// in all versions that support applets, these are ignored
|
// versions that support applets, these are ignored
|
||||||
MethodHandle method = ReflectionUtils.findMainMethod(main);
|
MethodHandle method = ReflectionUtils.findMainMethod(main);
|
||||||
method.invokeExact(gameArgs.toArray(new String[0]));
|
method.invokeExact(gameArgs.toArray(new String[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Applet createAppletClass(String clazz) throws Throwable {
|
||||||
|
Class<?> appletClass = ClassLoader.getSystemClassLoader().loadClass(clazz);
|
||||||
|
|
||||||
|
MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class));
|
||||||
|
return (Applet) appletConstructor.invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field findMinecraftGameDirField(Class<?> clazz) {
|
||||||
|
// search for private static File
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
if (field.getType() != File.class)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int fieldModifiers = field.getModifiers();
|
||||||
|
|
||||||
|
if (!Modifier.isStatic(fieldModifiers))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!Modifier.isPrivate(fieldModifiers))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Modifier.isFinal(fieldModifiers))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy;
|
||||||
|
|
||||||
|
import org.prismlauncher.launcher.Launcher;
|
||||||
|
import org.prismlauncher.legacy.fix.online.OnlineFixes;
|
||||||
|
import org.prismlauncher.utils.Parameters;
|
||||||
|
|
||||||
|
// implementation of LegacyProxy
|
||||||
|
public final class LegacyProxy {
|
||||||
|
public static Launcher createLauncher(Parameters params) {
|
||||||
|
return new LegacyLauncher(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyOnlineFixes(Parameters parameters) {
|
||||||
|
OnlineFixes.apply(parameters);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.fix.online;
|
||||||
|
|
||||||
|
import org.prismlauncher.legacy.utils.url.UrlUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
|
||||||
|
final class Handler extends URLStreamHandler {
|
||||||
|
@Override
|
||||||
|
protected URLConnection openConnection(URL address) throws IOException {
|
||||||
|
return openConnection(address, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URLConnection openConnection(URL address, Proxy proxy) throws IOException {
|
||||||
|
URLConnection result;
|
||||||
|
|
||||||
|
// try skin fix
|
||||||
|
result = SkinFix.openConnection(address, proxy);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return UrlUtils.openConnection(address, proxy);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.fix.online;
|
||||||
|
|
||||||
|
import org.prismlauncher.legacy.utils.Base64;
|
||||||
|
import org.prismlauncher.legacy.utils.url.UrlUtils;
|
||||||
|
import org.prismlauncher.utils.Parameters;
|
||||||
|
import org.prismlauncher.utils.logging.Log;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
import java.net.URLStreamHandlerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes skins by redirecting to other URLs.
|
||||||
|
*
|
||||||
|
* @see {@link Handler}
|
||||||
|
* @see {@link UrlUtils}
|
||||||
|
*/
|
||||||
|
public final class OnlineFixes implements URLStreamHandlerFactory {
|
||||||
|
public static void apply(Parameters params) {
|
||||||
|
if (!"true".equals(params.getString("onlineFixes", null)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!UrlUtils.isSupported() || !Base64.isSupported()) {
|
||||||
|
Log.warning("Cannot access the necessary Java internals for skin fix");
|
||||||
|
Log.warning("Turning off online fixes in the settings will silence the warnings");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL.setURLStreamHandlerFactory(new OnlineFixes());
|
||||||
|
} catch (Error e) {
|
||||||
|
Log.warning("Cannot apply skin fix: URLStreamHandlerFactory is already set");
|
||||||
|
Log.warning("Turning off online fixes in the settings will silence the warnings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URLStreamHandler createURLStreamHandler(String protocol) {
|
||||||
|
if ("http".equals(protocol))
|
||||||
|
return new Handler();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.fix.online;
|
||||||
|
|
||||||
|
import org.prismlauncher.legacy.utils.api.MojangApi;
|
||||||
|
import org.prismlauncher.legacy.utils.api.Texture;
|
||||||
|
import org.prismlauncher.legacy.utils.url.CustomUrlConnection;
|
||||||
|
import org.prismlauncher.legacy.utils.url.UrlUtils;
|
||||||
|
|
||||||
|
import java.awt.AlphaComposite;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
final class SkinFix {
|
||||||
|
static URLConnection openConnection(URL address, Proxy proxy) throws IOException {
|
||||||
|
String skinOwner = findSkinOwner(address);
|
||||||
|
if (skinOwner != null)
|
||||||
|
// we need to correct the skin
|
||||||
|
return getSkinConnection(skinOwner, proxy);
|
||||||
|
|
||||||
|
String capeOwner = findCapeOwner(address);
|
||||||
|
if (capeOwner != null) {
|
||||||
|
// since we do not need to process the image, open a direct connection bypassing
|
||||||
|
// Handler
|
||||||
|
Texture texture = MojangApi.getTexture(MojangApi.getUuid(capeOwner), "CAPE");
|
||||||
|
if (texture == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return UrlUtils.openConnection(texture.getUrl(), proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URLConnection getSkinConnection(String owner, Proxy proxy) throws IOException {
|
||||||
|
Texture texture = MojangApi.getTexture(MojangApi.getUuid(owner), "SKIN");
|
||||||
|
if (texture == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
URLConnection connection = UrlUtils.openConnection(texture.getUrl(), proxy);
|
||||||
|
try (InputStream in = connection.getInputStream()) {
|
||||||
|
// thank you craftycodie!
|
||||||
|
// this is heavily based on
|
||||||
|
// https://github.com/craftycodie/MineOnline/blob/4f4f86f9d051e0a6fd7ff0b95b2a05f7437683d7/src/main/java/gg/codie/mineonline/gui/textures/TextureHelper.java#L17
|
||||||
|
BufferedImage image = ImageIO.read(in);
|
||||||
|
Graphics2D graphics = image.createGraphics();
|
||||||
|
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
|
||||||
|
|
||||||
|
BufferedImage subimage;
|
||||||
|
|
||||||
|
if (image.getHeight() > 32) {
|
||||||
|
// flatten second layers
|
||||||
|
subimage = image.getSubimage(0, 32, 56, 16);
|
||||||
|
graphics.drawImage(subimage, 0, 16, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture.isSlim()) {
|
||||||
|
// convert slim to classic
|
||||||
|
subimage = image.getSubimage(45, 16, 9, 16);
|
||||||
|
graphics.drawImage(subimage, 46, 16, null);
|
||||||
|
|
||||||
|
subimage = image.getSubimage(49, 16, 2, 4);
|
||||||
|
graphics.drawImage(subimage, 50, 16, null);
|
||||||
|
|
||||||
|
subimage = image.getSubimage(53, 20, 2, 12);
|
||||||
|
graphics.drawImage(subimage, 54, 20, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.dispose();
|
||||||
|
|
||||||
|
// crop the image - old versions disregard all secondary layers besides the hat
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
image = image.getSubimage(0, 0, 64, 32);
|
||||||
|
ImageIO.write(image, "png", out);
|
||||||
|
|
||||||
|
return new CustomUrlConnection(out.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String findSkinOwner(URL address) {
|
||||||
|
switch (address.getHost()) {
|
||||||
|
case "www.minecraft.net":
|
||||||
|
return stripIfPrefixed(address.getPath(), "/skin/");
|
||||||
|
|
||||||
|
case "s3.amazonaws.com":
|
||||||
|
case "skins.minecraft.net":
|
||||||
|
return stripIfPrefixed(address.getPath(), "/MinecraftSkins/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String findCapeOwner(URL address) {
|
||||||
|
switch (address.getHost()) {
|
||||||
|
case "www.minecraft.net":
|
||||||
|
if (!address.getPath().equals("/cloak/get.jsp"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return stripIfPrefixed(address.getQuery(), "user=");
|
||||||
|
|
||||||
|
case "s3.amazonaws.com":
|
||||||
|
case "skins.minecraft.net":
|
||||||
|
return stripIfPrefixed(address.getPath(), "/MinecraftCloaks/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripIfPrefixed(String string, String prefix) {
|
||||||
|
if (string != null && string.startsWith(prefix)) {
|
||||||
|
string = string.substring(prefix.length());
|
||||||
|
|
||||||
|
if (string.endsWith(".png"))
|
||||||
|
string = string.substring(0, string.lastIndexOf('.'));
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils;
|
||||||
|
|
||||||
|
import org.prismlauncher.utils.logging.Log;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses Base64 with Java 8 or later, otherwise DatatypeConverter. In the latter
|
||||||
|
* case, reflection is used to allow using newer compilers.
|
||||||
|
*/
|
||||||
|
public final class Base64 {
|
||||||
|
private static boolean supported = true;
|
||||||
|
private static MethodHandle legacy;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
Class.forName("java.util.Base64");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
try {
|
||||||
|
Class<?> datatypeConverter = Class.forName("javax.xml.bind.DatatypeConverter");
|
||||||
|
legacy = MethodHandles.lookup().findStatic(
|
||||||
|
datatypeConverter, "parseBase64Binary", MethodType.methodType(byte[].class, String.class));
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e1) {
|
||||||
|
Log.error("Base64 not supported", e1);
|
||||||
|
supported = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether base64 is supported.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if base64 can be parsed
|
||||||
|
*/
|
||||||
|
public static boolean isSupported() {
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decode(String input) {
|
||||||
|
if (!isSupported())
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
if (legacy == null)
|
||||||
|
return java.util.Base64.getDecoder().decode(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (byte[]) legacy.invokeExact(input);
|
||||||
|
} catch (Error | RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils.api;
|
||||||
|
|
||||||
|
import org.prismlauncher.legacy.utils.Base64;
|
||||||
|
import org.prismlauncher.legacy.utils.json.JsonParser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic wrapper for Mojang's Minecraft API.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final class MojangApi {
|
||||||
|
public static String getUuid(String username) throws IOException {
|
||||||
|
try (InputStream in = new URL("https://api.mojang.com/users/profiles/minecraft/" + username).openStream()) {
|
||||||
|
Map<String, Object> map = (Map<String, Object>) JsonParser.parse(in);
|
||||||
|
return (String) map.get("id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Texture getTexture(String player, String id) throws IOException {
|
||||||
|
Map<String, Object> map = getTextures(player);
|
||||||
|
|
||||||
|
if (map != null) {
|
||||||
|
map = (Map<String, Object>) map.get(id);
|
||||||
|
if (map == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
URL url = new URL((String) map.get("url"));
|
||||||
|
boolean slim = false;
|
||||||
|
|
||||||
|
if (id.equals("SKIN")) {
|
||||||
|
map = (Map<String, Object>) map.get("metadata");
|
||||||
|
if (map != null && "slim".equals(map.get("model")))
|
||||||
|
slim = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Texture(url, slim);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> getTextures(String player) throws IOException {
|
||||||
|
try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + player).openStream()) {
|
||||||
|
Map<String, Object> profile = (Map<String, Object>) JsonParser.parse(profileIn);
|
||||||
|
|
||||||
|
for (Map<String, Object> property : (Iterable<Map<String, Object>>) profile.get("properties")) {
|
||||||
|
if (property.get("name").equals("textures")) {
|
||||||
|
Map<String, Object> result =
|
||||||
|
(Map<String, Object>) JsonParser.parse(new String(Base64.decode((String) property.get("value"))));
|
||||||
|
result = (Map<String, Object>) result.get("textures");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils.api;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a texture from the Mojang API.
|
||||||
|
*/
|
||||||
|
public final class Texture {
|
||||||
|
private final URL url;
|
||||||
|
private final boolean slim;
|
||||||
|
|
||||||
|
public Texture(URL url, boolean slim) {
|
||||||
|
this.url = url;
|
||||||
|
this.slim = slim;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSlim() {
|
||||||
|
return slim;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils.json;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public final class JsonParseException extends IOException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public JsonParseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,408 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils.json;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lightweight portable JSON parser used instead of GSON since it is not
|
||||||
|
* available in a lot of versions.
|
||||||
|
*/
|
||||||
|
public final class JsonParser {
|
||||||
|
private final Reader in;
|
||||||
|
private char[] buffer;
|
||||||
|
private int pos, length;
|
||||||
|
|
||||||
|
public static Object parse(String in) throws IOException {
|
||||||
|
return parse(new StringReader(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object parse(InputStream in) throws IOException {
|
||||||
|
return parse(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object parse(Reader in) throws IOException {
|
||||||
|
return new JsonParser(in).readSingleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonParser(Reader in) throws IOException {
|
||||||
|
this.in = in;
|
||||||
|
pos = length = 0;
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int character() {
|
||||||
|
if (length == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return buffer[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
private int read() throws IOException {
|
||||||
|
if (length == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (buffer == null || pos++ == length - 1) {
|
||||||
|
pos = 0;
|
||||||
|
buffer = new char[8192];
|
||||||
|
length = in.read(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return character();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCharacter(char character) throws JsonParseException {
|
||||||
|
if (character() != character)
|
||||||
|
throw new JsonParseException(
|
||||||
|
"Expected '" + character + "' but got " + (character() != -1 ? ("'" + (char) character() + "'") : "EOF"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoEOF(String expected) throws JsonParseException {
|
||||||
|
if (character() == -1)
|
||||||
|
throw new JsonParseException("Expected " + expected + " but got EOF");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipWhitespace() throws IOException {
|
||||||
|
while (isWhitespace()) read();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWhitespace() {
|
||||||
|
return character() == ' ' || character() == '\n' || character() == '\r' || character() == '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readSingleValue() throws IOException {
|
||||||
|
skipWhitespace();
|
||||||
|
Object result = readValue();
|
||||||
|
|
||||||
|
if (!(result instanceof Double))
|
||||||
|
read();
|
||||||
|
|
||||||
|
skipWhitespace();
|
||||||
|
|
||||||
|
if (character() != -1)
|
||||||
|
throw new JsonParseException("Found trailing non-whitespace characters");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readValue() throws IOException {
|
||||||
|
assertNoEOF("a value");
|
||||||
|
|
||||||
|
int character = character();
|
||||||
|
|
||||||
|
switch (character) {
|
||||||
|
case '{':
|
||||||
|
return readObject();
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
return readArray();
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
return readString();
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
case 'f':
|
||||||
|
// probably boolean
|
||||||
|
Boolean bool = readBoolean();
|
||||||
|
if (bool != null)
|
||||||
|
return bool;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
// probably null
|
||||||
|
if (readNull())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == '-' || isDigit())
|
||||||
|
// probably a number
|
||||||
|
return readNumber();
|
||||||
|
|
||||||
|
throw new JsonParseException("Expected a JSON value but got '" + (char) character + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> readObject() throws IOException {
|
||||||
|
assertCharacter('{');
|
||||||
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
boolean comma = false;
|
||||||
|
|
||||||
|
read();
|
||||||
|
skipWhitespace();
|
||||||
|
|
||||||
|
while (character() != '}') {
|
||||||
|
if (comma) {
|
||||||
|
assertCharacter(',');
|
||||||
|
read();
|
||||||
|
skipWhitespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = readString();
|
||||||
|
read();
|
||||||
|
skipWhitespace();
|
||||||
|
assertCharacter(':');
|
||||||
|
read();
|
||||||
|
skipWhitespace();
|
||||||
|
|
||||||
|
Object value = readValue();
|
||||||
|
obj.put(key, value);
|
||||||
|
|
||||||
|
if (!(value instanceof Double))
|
||||||
|
read();
|
||||||
|
|
||||||
|
skipWhitespace();
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Object> readArray() throws IOException {
|
||||||
|
assertCharacter('[');
|
||||||
|
List<Object> array = new ArrayList<>();
|
||||||
|
boolean comma = false;
|
||||||
|
|
||||||
|
read();
|
||||||
|
skipWhitespace();
|
||||||
|
|
||||||
|
while (character() != ']') {
|
||||||
|
if (comma) {
|
||||||
|
assertCharacter(',');
|
||||||
|
read();
|
||||||
|
skipWhitespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value = readValue();
|
||||||
|
array.add(value);
|
||||||
|
|
||||||
|
if (!(value instanceof Double))
|
||||||
|
read();
|
||||||
|
|
||||||
|
skipWhitespace();
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readString() throws IOException {
|
||||||
|
assertCharacter('"');
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
while (read() != '"') {
|
||||||
|
int character = character();
|
||||||
|
|
||||||
|
if (character >= '\u0000' && character <= '\u001F')
|
||||||
|
throw new JsonParseException("Found unescaped control character within string");
|
||||||
|
|
||||||
|
switch (character) {
|
||||||
|
case -1:
|
||||||
|
throw new JsonParseException("Expected '\"' but got EOF");
|
||||||
|
|
||||||
|
case 0x7F:
|
||||||
|
if (read() == '"') {
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
int seq = read();
|
||||||
|
|
||||||
|
switch (seq) {
|
||||||
|
case -1:
|
||||||
|
throw new JsonParseException("Expected an escape sequence but got EOF");
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
case '\"':
|
||||||
|
character = seq;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
character = '\b';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
character = '\f';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
character = '\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
character = '\r';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
character = '\t';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
// char array to allow allocation in advance.
|
||||||
|
char[] digits = new char[4];
|
||||||
|
|
||||||
|
for (int index = 0; index < digits.length; index++) {
|
||||||
|
character = read();
|
||||||
|
if (index == 0 && character() == '-') {
|
||||||
|
throw new JsonParseException("Hex sequence may not be negative");
|
||||||
|
} else if (character() == -1) {
|
||||||
|
throw new JsonParseException("Expected a hex sequence but got EOF");
|
||||||
|
}
|
||||||
|
digits[index] = (char) character;
|
||||||
|
}
|
||||||
|
|
||||||
|
String digitsString = new String(digits);
|
||||||
|
|
||||||
|
try {
|
||||||
|
character = Integer.parseInt(digitsString, 16);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new JsonParseException("Could not parse hex sequence \"" + digitsString + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new JsonParseException("Invalid escape sequence: \\" + (char) seq);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append((char) character);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDigit() {
|
||||||
|
return character() >= '0' && character() <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
private Double readNumber() throws IOException {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
if (character() == '-') {
|
||||||
|
result.append((char) character());
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character() == '0') {
|
||||||
|
result.append((char) character());
|
||||||
|
read();
|
||||||
|
|
||||||
|
if (isDigit())
|
||||||
|
throw new JsonParseException("Found superfluous leading zero");
|
||||||
|
} else if (!isDigit())
|
||||||
|
throw new JsonParseException("Expected digits");
|
||||||
|
|
||||||
|
while (character() != -1 && isDigit()) {
|
||||||
|
result.append((char) character());
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character() == '.') {
|
||||||
|
result.append('.');
|
||||||
|
|
||||||
|
read();
|
||||||
|
assertNoEOF("digits");
|
||||||
|
|
||||||
|
if (!isDigit())
|
||||||
|
throw new JsonParseException("Expected digits after decimal point");
|
||||||
|
|
||||||
|
while (character() != -1 && isDigit()) {
|
||||||
|
result.append((char) character());
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character() == 'e' || character() == 'E') {
|
||||||
|
result.append('E');
|
||||||
|
|
||||||
|
read();
|
||||||
|
assertNoEOF("digits");
|
||||||
|
|
||||||
|
if (character() == '+' || character() == '-') {
|
||||||
|
result.append((char) character());
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(character() == '+' || character() == '-' || isDigit()))
|
||||||
|
throw new JsonParseException("Expected exponent digits");
|
||||||
|
|
||||||
|
while (character() != -1 && isDigit()) {
|
||||||
|
result.append((char) character());
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String resultStr = result.toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(resultStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new JsonParseException("Failed to parse number '" + resultStr + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean readBoolean() throws IOException {
|
||||||
|
if (character() == 't') {
|
||||||
|
if (read() == 'r' && read() == 'u' && read() == 'e') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (character() == 'f' && read() == 'a' && read() == 'l' && read() == 's' && read() == 'e') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean readNull() throws IOException {
|
||||||
|
return character() == 'n' && read() == 'u' && read() == 'l' && read() == 'l';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils.url;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
|
public final class CustomUrlConnection extends HttpURLConnection {
|
||||||
|
private final InputStream in;
|
||||||
|
|
||||||
|
public CustomUrlConnection(byte[] data) {
|
||||||
|
this(new ByteArrayInputStream(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomUrlConnection(InputStream in) {
|
||||||
|
super(null);
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect() throws IOException {
|
||||||
|
responseCode = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean usingProxy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy.utils.url;
|
||||||
|
|
||||||
|
import org.prismlauncher.utils.logging.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for URLs which uses reflection to access constructors for
|
||||||
|
* internal classes.
|
||||||
|
*/
|
||||||
|
public final class UrlUtils {
|
||||||
|
private static URLStreamHandler http;
|
||||||
|
private static MethodHandle openConnection;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
// we first obtain the stock URLStreamHandler for http as we overwrite it later
|
||||||
|
Method getURLStreamHandler = URL.class.getDeclaredMethod("getURLStreamHandler", String.class);
|
||||||
|
getURLStreamHandler.setAccessible(true);
|
||||||
|
http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http");
|
||||||
|
|
||||||
|
// we next find the openConnection method
|
||||||
|
Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, Proxy.class);
|
||||||
|
openConnectionReflect.setAccessible(true);
|
||||||
|
openConnection = MethodHandles.lookup().unreflect(openConnectionReflect);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.error("URL reflection failed - some features may not work", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether all the features of this class are available.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if all features can be used
|
||||||
|
*/
|
||||||
|
public static boolean isSupported() {
|
||||||
|
return http != null && openConnection != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URLConnection openConnection(URL url, Proxy proxy) throws IOException {
|
||||||
|
if (http == null)
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
if (url.getProtocol().equals("http"))
|
||||||
|
return openConnection(http, url, proxy);
|
||||||
|
|
||||||
|
// fall back to Java's default method
|
||||||
|
// at this point, this should not cause a StackOverflowError unless we've missed
|
||||||
|
// a protocol out from the if statements
|
||||||
|
return url.openConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException {
|
||||||
|
if (openConnection == null)
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (URLConnection) openConnection.invokeExact(handler, url, proxy);
|
||||||
|
} catch (IOException | Error | RuntimeException e) {
|
||||||
|
throw e; // rethrow if possible
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new AssertionError(e); // oh dear! this isn't meant to happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,7 +57,7 @@ package org.prismlauncher;
|
|||||||
import org.prismlauncher.exception.ParseException;
|
import org.prismlauncher.exception.ParseException;
|
||||||
import org.prismlauncher.launcher.Launcher;
|
import org.prismlauncher.launcher.Launcher;
|
||||||
import org.prismlauncher.launcher.impl.StandardLauncher;
|
import org.prismlauncher.launcher.impl.StandardLauncher;
|
||||||
import org.prismlauncher.launcher.impl.legacy.LegacyLauncher;
|
import org.prismlauncher.legacy.LegacyProxy;
|
||||||
import org.prismlauncher.utils.Parameters;
|
import org.prismlauncher.utils.Parameters;
|
||||||
import org.prismlauncher.utils.logging.Log;
|
import org.prismlauncher.utils.logging.Log;
|
||||||
|
|
||||||
@ -106,6 +106,8 @@ public final class EntryPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
LegacyProxy.applyOnlineFixes(params);
|
||||||
|
|
||||||
Launcher launcher;
|
Launcher launcher;
|
||||||
String type = params.getString("launcher");
|
String type = params.getString("launcher");
|
||||||
|
|
||||||
@ -115,7 +117,7 @@ public final class EntryPoint {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "legacy":
|
case "legacy":
|
||||||
launcher = new LegacyLauncher(params);
|
launcher = LegacyProxy.createLauncher(params);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
66
libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java
Normal file
66
libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Linking this library statically or dynamically with other modules is
|
||||||
|
* making a combined work based on this library. Thus, the terms and
|
||||||
|
* conditions of the GNU General Public License cover the whole
|
||||||
|
* combination.
|
||||||
|
*
|
||||||
|
* As a special exception, the copyright holders of this library give
|
||||||
|
* you permission to link this library with independent modules to
|
||||||
|
* produce an executable, regardless of the license terms of these
|
||||||
|
* independent modules, and to copy and distribute the resulting
|
||||||
|
* executable under terms of your choice, provided that you also meet,
|
||||||
|
* for each linked independent module, the terms and conditions of the
|
||||||
|
* license of that module. An independent module is a module which is
|
||||||
|
* not derived from or based on this library. If you modify this
|
||||||
|
* library, you may extend this exception to your version of the
|
||||||
|
* library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
* so, delete this exception statement from your version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.prismlauncher.legacy;
|
||||||
|
|
||||||
|
import org.prismlauncher.launcher.Launcher;
|
||||||
|
import org.prismlauncher.utils.Parameters;
|
||||||
|
|
||||||
|
// used as a fallback if NewLaunchLegacy is not on the classpath
|
||||||
|
// if it is, this class will be replaced
|
||||||
|
public final class LegacyProxy {
|
||||||
|
public static Launcher createLauncher(Parameters params) {
|
||||||
|
throw new AssertionError("NewLaunchLegacy is not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void applyOnlineFixes(Parameters params) {}
|
||||||
|
}
|
@ -68,61 +68,6 @@ public final class ReflectionUtils {
|
|||||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||||
private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
|
private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader();
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a Java applet by its class name.
|
|
||||||
*
|
|
||||||
* @param clazz The class name
|
|
||||||
* @return The applet instance
|
|
||||||
* @throws Throwable
|
|
||||||
*/
|
|
||||||
public static Applet createAppletClass(String clazz) throws Throwable {
|
|
||||||
Class<?> appletClass = LOADER.loadClass(clazz);
|
|
||||||
|
|
||||||
MethodHandle appletConstructor = LOOKUP.findConstructor(appletClass, MethodType.methodType(void.class));
|
|
||||||
return (Applet) appletConstructor.invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Best guess of the game directory field within net.minecraft.client.Minecraft.
|
|
||||||
* Designed for legacy versions - newer versions do not use a static field.
|
|
||||||
*
|
|
||||||
* @param clazz The class
|
|
||||||
* @return The first field matching criteria
|
|
||||||
*/
|
|
||||||
public static Field findMinecraftGameDirField(Class<?> clazz) {
|
|
||||||
Log.debug("Resolving minecraft game directory field");
|
|
||||||
|
|
||||||
// search for private static File
|
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
|
||||||
if (field.getType() != File.class) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fieldModifiers = field.getModifiers();
|
|
||||||
|
|
||||||
if (!Modifier.isStatic(fieldModifiers)) {
|
|
||||||
Log.debug("Rejecting field " + field.getName() + " because it is not static");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Modifier.isPrivate(fieldModifiers)) {
|
|
||||||
Log.debug("Rejecting field " + field.getName() + " because it is not private");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Modifier.isFinal(fieldModifiers)) {
|
|
||||||
Log.debug("Rejecting field " + field.getName() + " because it is final");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.debug("Identified field " + field.getName() + " to match conditions for game directory field");
|
|
||||||
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the main method within a class.
|
* Gets the main method within a class.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user