WIP updates for Minecraft 1.21. I have a headache.

This commit is contained in:
flash 2024-07-10 02:05:37 +02:00
parent fbd20ab20f
commit 38794109c8
19 changed files with 159 additions and 104 deletions

8
.gitattributes vendored Normal file
View file

@ -0,0 +1,8 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf

View file

@ -1,4 +1,4 @@
Copyright (c) 2023, flashwave <me@flash.moe> Copyright (c) 2023-2024, flashwave <me@flash.moe>
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View file

@ -1,5 +1,5 @@
plugins { plugins {
id 'fabric-loom' version '1.3-SNAPSHOT' id 'fabric-loom' version '1.7-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
} }
@ -29,15 +29,15 @@ processResources {
tasks.withType(JavaCompile).configureEach { tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8" it.options.encoding = "UTF-8"
it.options.release = 17 it.options.release = 21
it.options.compilerArgs += ['-Xlint:deprecation', '-Xlint:unchecked'] it.options.compilerArgs += ['-Xlint:deprecation', '-Xlint:unchecked']
} }
java { java {
withSourcesJar() withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_21
} }
jar { jar {

View file

@ -4,11 +4,11 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.20.1 minecraft_version=1.21
yarn_mappings=1.20.1+build.10 yarn_mappings=1.21+build.8
loader_version=0.14.22 loader_version=0.15.11
# Mod Properties # Mod Properties
mod_version=1.0.1 mod_version=1.1.0
maven_group=net.flashii.mcexts maven_group=net.flashii.mcexts
archives_base_name=flashii-extensions archives_base_name=flashii-extensions

Binary file not shown.

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

19
gradlew vendored
View file

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -83,7 +83,8 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -201,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command; # Collect all arguments for the java command:
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# shell script including quotes and variable substitutions, so put them in # and any embedded shellness will be escaped.
# double quotes to make sure that they get re-expanded; and # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# * put everything else in single quotes, so that it's not re-expanded. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \

20
gradlew.bat vendored
View file

@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail

View file

@ -8,8 +8,12 @@ import java.net.HttpURLConnection;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.URL; import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Base64; import java.util.Base64;
@ -67,48 +71,52 @@ public class RPC {
// sigStr and bodyStr because sigStr must also include the query params if those are present // sigStr and bodyStr because sigStr must also include the query params if those are present
public static RPCPayload callRpc(String path, String sigStr, String bodyStr) public static RPCPayload callRpc(String path, String sigStr, String bodyStr)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException, InterruptedException {
boolean hasBody = bodyStr != null; boolean hasBody = bodyStr != null;
String time = getRequestTimestamp(); String time = getRequestTimestamp();
String hash = createRequestSignature(time, URLs.getRpcPath(path), sigStr); String hash = createRequestSignature(time, URLs.getRpcPath(path), sigStr);
HttpURLConnection conn = (HttpURLConnection)(new URL(URLs.getRpcUrl(path))).openConnection(); URI url;
conn.setRequestMethod(hasBody ? "POST" : "GET"); try {
conn.setRequestProperty("X-Mince-Time", time); url = new URI(URLs.getRpcUrl(path));
conn.setRequestProperty("X-Mince-Hash", hash); } catch(URISyntaxException ex) {
// fuck it
if(hasBody) { throw new IOException(ex.getMessage());
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try(OutputStream stream = conn.getOutputStream()) {
byte[] input = bodyStr.getBytes(StandardCharsets.UTF_8);
stream.write(input, 0, input.length);
}
} }
StringBuilder sb = new StringBuilder(); HttpClient client = HttpClient.newHttpClient();
try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
String line; .uri(url)
while((line = br.readLine()) != null) .header("X-Mince-Time", time)
sb.append(line); .header("X-Mince-Hash", hash);
}
return getGson().fromJson(sb.toString(), RPCPayload.class); if(hasBody)
requestBuilder.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(bodyStr, StandardCharsets.UTF_8));
else
requestBuilder.GET();
HttpRequest request = requestBuilder.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if(response.statusCode() != 200)
throw new IOException("RPC request failed: " + response.statusCode());
return getGson().fromJson(response.body(), RPCPayload.class);
} }
public static RPCPayload callRpc(String path, String paramStr) public static RPCPayload callRpc(String path, String paramStr)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException, InterruptedException {
return callRpc(path, paramStr, paramStr); return callRpc(path, paramStr, paramStr);
} }
public static RPCPayload callRpc(String path, Map<String, Object> params) public static RPCPayload callRpc(String path, Map<String, Object> params)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException, InterruptedException {
return callRpc(path, createParamString(params)); return callRpc(path, createParamString(params));
} }
public static RPCPayload postAuth(UUID id, String name, InetAddress remoteAddr) public static RPCPayload postAuth(UUID id, String name, InetAddress remoteAddr)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException, InterruptedException {
HashMap<String, Object> params = new HashMap<>(); HashMap<String, Object> params = new HashMap<>();
params.put("id", id); params.put("id", id);
params.put("name", name); params.put("name", name);
@ -118,7 +126,7 @@ public class RPC {
} }
public static RPCPayload postAuth(UUID id, String name, SocketAddress sockAddr) public static RPCPayload postAuth(UUID id, String name, SocketAddress sockAddr)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException, InterruptedException {
InetAddress remoteAddr = sockAddr instanceof InetSocketAddress InetAddress remoteAddr = sockAddr instanceof InetSocketAddress
? ((InetSocketAddress)sockAddr).getAddress() ? ((InetSocketAddress)sockAddr).getAddress()
: InetAddress.getLoopbackAddress(); : InetAddress.getLoopbackAddress();

View file

@ -1,16 +0,0 @@
package net.flashii.mcexts.mixin;
import net.flashii.mcexts.URLs;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = "com.mojang.authlib.Environment$1")
public abstract class EnvironmentMixin {
@Inject(method = "getSessionHost()Ljava/lang/String;", at = @At("HEAD"), cancellable = true, remap = false)
private void getSessionHost(CallbackInfoReturnable<String> cir) {
cir.setReturnValue(URLs.getSessionHost());
cir.cancel();
}
}

View file

@ -8,7 +8,13 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(PlayerListHud.class) @Mixin(PlayerListHud.class)
public abstract class PlayerListHudMixin { public abstract class PlayerListHudMixin {
@Redirect(method = "render(Lnet/minecraft/client/gui/DrawContext;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreboardObjective;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;isInSingleplayer()Z")) @Redirect(
method = "render(Lnet/minecraft/client/gui/DrawContext;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreboardObjective;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/MinecraftClient;isInSingleplayer()Z"
)
)
public boolean redirectIsInSinglePlayer(MinecraftClient client) { public boolean redirectIsInSinglePlayer(MinecraftClient client) {
// always enable BL // always enable BL
return true; return true;

View file

@ -16,7 +16,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerManager.class) @Mixin(PlayerManager.class)
public abstract class PlayerManagerMixin { public abstract class PlayerManagerMixin {
@Inject(method = "checkCanJoin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/text/Text;", at = @At("HEAD"), cancellable = true) @Inject(
method = "checkCanJoin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/text/Text;",
at = @At("HEAD"),
cancellable = true
)
private void checkCanJoin(SocketAddress sockAddr, GameProfile profile, CallbackInfoReturnable<Text> cir) { private void checkCanJoin(SocketAddress sockAddr, GameProfile profile, CallbackInfoReturnable<Text> cir) {
Text authText = null; Text authText = null;
@ -60,6 +64,9 @@ public abstract class PlayerManagerMixin {
} catch(GeneralSecurityException ex) { } catch(GeneralSecurityException ex) {
authText = Text.literal("Problem with request verification, yell at flashwave about this.").formatted(Formatting.RED); authText = Text.literal("Problem with request verification, yell at flashwave about this.").formatted(Formatting.RED);
ex.printStackTrace(); ex.printStackTrace();
} catch(InterruptedException ex) {
authText = Text.literal("Problem with connecting to the Flashii authentication server, yell at flashwave about this.").formatted(Formatting.RED);
ex.printStackTrace();
} }
if(authText != null) { if(authText != null) {

View file

@ -0,0 +1,33 @@
package net.flashii.mcexts.mixin;
import com.mojang.authlib.GameProfile;
import java.util.concurrent.CompletableFuture;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.client.util.SkinTextures;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerSkinProvider.class)
public abstract class PlayerSkinProviderMixin {
@Inject(
method = "fetchSkinTextures(Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture;",
at = @At("RETURN"),
cancellable = true
)
private void modifyFetchSkinTexturesReturn(GameProfile profile, CallbackInfoReturnable<CompletableFuture<SkinTextures>> cir) {
CompletableFuture<SkinTextures> future = cir.getReturnValue();
cir.setReturnValue(future.thenApply(st -> {
return new SkinTextures(
st.texture(),
st.textureUrl(),
st.capeTexture(),
st.elytraTexture(),
st.model(),
true
);
}));
cir.cancel();
}
}

View file

@ -13,7 +13,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ServerMetadata.class) @Mixin(ServerMetadata.class)
public abstract class ServerMetadataMixin { public abstract class ServerMetadataMixin {
@Inject(method = "description()Lnet/minecraft/text/Text;", at = @At("TAIL"), cancellable = true) @Inject(
method = "description()Lnet/minecraft/text/Text;",
at = @At("TAIL"),
cancellable = true
)
public void description(CallbackInfoReturnable<Text> cir) { public void description(CallbackInfoReturnable<Text> cir) {
String linesRaw = Config.getValue("MOTDLines.txt"); String linesRaw = Config.getValue("MOTDLines.txt");
if(linesRaw == null || linesRaw.isBlank()) if(linesRaw == null || linesRaw.isBlank())

View file

@ -9,7 +9,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(TextureUrlChecker.class) @Mixin(TextureUrlChecker.class)
public abstract class TextureUrlCheckerMixin { public abstract class TextureUrlCheckerMixin {
@Inject(method = "isAllowedTextureDomain(Ljava/lang/String;)Z", at = @At("HEAD"), cancellable = true, remap = false) @Inject(
method = "isAllowedTextureDomain(Ljava/lang/String;)Z",
at = @At("HEAD"),
cancellable = true,
remap = false
)
private static void isAllowedTextureDomain(String url, CallbackInfoReturnable<Boolean> cir) { private static void isAllowedTextureDomain(String url, CallbackInfoReturnable<Boolean> cir) {
if(url == null || url.startsWith(URLs.getTexturesHostPrefix())) { if(url == null || url.startsWith(URLs.getTexturesHostPrefix())) {
cir.setReturnValue(true); cir.setReturnValue(true);

View file

@ -0,0 +1,23 @@
package net.flashii.mcexts.mixin;
import com.mojang.authlib.Environment;
import com.mojang.authlib.yggdrasil.YggdrasilEnvironment;
import net.flashii.mcexts.URLs;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(YggdrasilEnvironment.class)
public abstract class YggdrasilEnvironmentMixin {
@Redirect(
method = "<init>",
at = @At(
value = "NEW",
target = "com/mojang/authlib/Environment",
ordinal = 0
)
)
private Environment redirectEnvironmentCreation(String sessionHost, String servicesHost, String name) {
return new Environment(URLs.getSessionHost(), servicesHost, name);
}
}

View file

@ -1,24 +0,0 @@
package net.flashii.mcexts.mixin;
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(YggdrasilMinecraftSessionService.class)
public abstract class YggdrasilMinecraftSessionServiceMixin {
@ModifyVariable(method = "getTextures(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", at = @At("HEAD"), argsOnly = true, remap = false)
private boolean interceptGetTexturesRequireSecure(boolean requireSecure) {
return false;
}
@ModifyVariable(method = "fillProfileProperties(Lcom/mojang/authlib/GameProfile;Z)Lcom/mojang/authlib/GameProfile;", at = @At("HEAD"), argsOnly = true, remap = false)
private boolean interceptFillProfilePropertiesRequireSecure(boolean requireSecure) {
return true;
}
@ModifyVariable(method = "fillGameProfile(Lcom/mojang/authlib/GameProfile;Z)Lcom/mojang/authlib/GameProfile;", at = @At("HEAD"), argsOnly = true, remap = false)
private boolean interceptFillGameProfileRequireSecure(boolean requireSecure) {
return false;
}
}

View file

@ -19,7 +19,7 @@
], ],
"depends": { "depends": {
"fabricloader": ">=0.14.22", "fabricloader": ">=0.14.22",
"minecraft": "~1.20.1", "minecraft": ">=1.21",
"java": ">=17" "java": ">=21"
} }
} }

View file

@ -1,12 +1,12 @@
{ {
"required": true, "required": true,
"package": "net.flashii.mcexts.mixin", "package": "net.flashii.mcexts.mixin",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_21",
"mixins": [ "mixins": [
"EnvironmentMixin", "PlayerSkinProviderMixin",
"PlayerListHudMixin", "PlayerListHudMixin",
"TextureUrlCheckerMixin", "TextureUrlCheckerMixin",
"YggdrasilMinecraftSessionServiceMixin" "YggdrasilEnvironmentMixin"
], ],
"server": [ "server": [
"PlayerManagerMixin", "PlayerManagerMixin",