diff --git a/build.gradle b/build.gradle index b85b3c7..8ee7e2d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,10 @@ plugins { + id 'java-library' id 'eclipse' id 'idea' id 'maven-publish' - id 'net.neoforged.gradle' version '[6.0.18,6.2)' - id 'org.parchmentmc.librarian.forgegradle' version '1.+' + id 'net.neoforged.gradle.userdev' version '7.0.192' +// id 'org.parchmentmc.librarian.forgegradle' version '1.+' } if (System.getenv('VERSION') != null) { @@ -25,67 +26,77 @@ base { archivesName = mod_id } -java.toolchain.languageVersion = JavaLanguageVersion.of(17) - -println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" -minecraft { - mappings channel: mapping_channel, version: mapping_version - - copyIdeResources = true - accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') - runs { - configureEach { - workingDirectory project.file('run') - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - - mods { - "${mod_id}" { - source sourceSets.main - } +repositories { + exclusiveContent { + forRepository { + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" } } - - client { - property 'forge.enabledGameTestNamespaces', mod_id + filter { + includeGroup "maven.modrinth" } + } +} - server { - property 'forge.enabledGameTestNamespaces', mod_id - args '--nogui' - } +java.toolchain.languageVersion = JavaLanguageVersion.of(21) - gameTestServer { - property 'forge.enabledGameTestNamespaces', mod_id - } +println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" - data { - workingDirectory project.file('run-data') +minecraft.accessTransformers.file file('src/main/resources/META-INF/accesstransformer.cfg') - args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') - } +runs { + configureEach { + modSource sourceSets.main + workingDirectory project.file('run') + systemProperty 'forge.logging.markers', 'REGISTRIES' + systemProperty 'forge.logging.console.level', 'debug' + + //mods { + // "${mod_id}" { + // source sourceSets.main + // } + //} + } + + client { + systemProperty 'forge.enabledGameTestNamespaces', mod_id + } + + server { + systemProperty 'forge.enabledGameTestNamespaces', mod_id + programArguments.addAll '--nogui' + } + + gameTestServer { + systemProperty 'forge.enabledGameTestNamespaces', mod_id + } + + data { + workingDirectory project.file('run-data') + programArguments.addAll '--mod', mod_id, '--all', '--output', 'src/generated/resources/', '--existing', 'src/main/resources/' } } sourceSets.main.resources { srcDir 'src/generated/resources' } -repositories {} - dependencies { - minecraft "net.neoforged:forge:${minecraft_version}-${forge_version}" + implementation "net.neoforged:neoforge:${neoforge_version}" + localRuntime "maven.modrinth:no-chat-reports:NeoForge-1.21.1-v2.9.1" } tasks.named('processResources', ProcessResources).configure { var replaceProperties = [ minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range, - forge_version : forge_version, forge_version_range: forge_version_range, + neoforge_version : neoforge_version, neoforge_version_range: neoforge_version_range, loader_version_range: loader_version_range, mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, mod_authors : mod_authors, mod_description: mod_description, ] inputs.properties replaceProperties - filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) { + filesMatching(['META-INF/neoforge.mods.toml', 'pack.mcmeta']) { expand replaceProperties + [project: project] } } @@ -103,7 +114,7 @@ tasks.named('jar', Jar).configure { ]) } - finalizedBy 'reobfJar' +// finalizedBy 'reobfJar' } publishing { diff --git a/gradle.properties b/gradle.properties index 3c5b559..728e2be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,14 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -minecraft_version=1.20.1 -minecraft_version_range=[1.20.1,1.21) -forge_version=47.1.54 -forge_version_range=[47,) -loader_version_range=[47,) -mapping_channel=parchment -mapping_version=2023.07.30-1.20.1 +minecraft_version=1.21.1 +minecraft_version_range=[1.21,1.21.2) +neoforge_version=21.1.97 +neoforge_version_range=[21.1,) +loader_version_range=[1.0,) +#mapping_channel=parchment +#mapping_version=2023.12.10-1.20.2 +neogradle.subsystems.parchment.minecraftVersion=1.21.1 +neogradle.subsystems.parchment.mappingsVersion=2024.11.17 mod_id=mutil mod_name=mutil mod_license=MIT diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cb..1b33c55 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d..d4081da 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 65dcd68..23d15a9 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (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. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,10 +85,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# 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"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -133,10 +133,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +147,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # 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 ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # 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" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,16 +200,20 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..5eed7ee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,92 +1,94 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle index caa335d..cfc6739 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,17 +1,17 @@ pluginManagement { repositories { + mavenLocal() gradlePluginPortal() maven { - name "NeoForge" url "https://maven.neoforged.net/releases/" } - maven { - name 'parchment' - url 'https://maven.parchmentmc.org' - } +// maven { +// name 'parchment' +// url 'https://maven.parchmentmc.org' +// } } } plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' -} \ No newline at end of file +} diff --git a/src/main/java/se/mickelus/mutil/ConfigHandler.java b/src/main/java/se/mickelus/mutil/ConfigHandler.java index 9413b41..6b3573a 100644 --- a/src/main/java/se/mickelus/mutil/ConfigHandler.java +++ b/src/main/java/se/mickelus/mutil/ConfigHandler.java @@ -1,44 +1,43 @@ package se.mickelus.mutil; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.FMLEnvironment; +import javax.annotation.ParametersAreNonnullByDefault; + import org.apache.commons.lang3.tuple.Pair; -import javax.annotation.ParametersAreNonnullByDefault; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.neoforge.common.ModConfigSpec; @ParametersAreNonnullByDefault -@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +//@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) class ConfigHandler { public static Client client; - static ForgeConfigSpec clientSpec; + static ModConfigSpec clientSpec; - public static void setup() { - if (FMLEnvironment.dist.isClient()) { + public static void setup(ModContainer container, Dist side) { + if (side.isClient()) { setupClient(); - ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, clientSpec); - FMLJavaModLoadingContext.get().getModEventBus().register(ConfigHandler.client); + container.registerConfig(ModConfig.Type.CLIENT, clientSpec); + //TODO: this might cause issues with config values + //FMLJavaModLoadingContext.get().getModEventBus().register(ConfigHandler.client); } } @OnlyIn(Dist.CLIENT) private static void setupClient() { - final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); + final Pair specPair = new ModConfigSpec.Builder().configure(Client::new); clientSpec = specPair.getRight(); client = specPair.getLeft(); } @OnlyIn(Dist.CLIENT) public static class Client { - public ForgeConfigSpec.BooleanValue queryPerks; + public ModConfigSpec.BooleanValue queryPerks; - Client(ForgeConfigSpec.Builder builder) { + Client(ModConfigSpec.Builder builder) { queryPerks = builder .comment("Controls if perks data should be queried on startup") .define("query_perks", true); diff --git a/src/main/java/se/mickelus/mutil/MUtilMod.java b/src/main/java/se/mickelus/mutil/MUtilMod.java index 78b348b..316c4d8 100644 --- a/src/main/java/se/mickelus/mutil/MUtilMod.java +++ b/src/main/java/se/mickelus/mutil/MUtilMod.java @@ -1,21 +1,26 @@ package se.mickelus.mutil; import net.minecraft.client.Minecraft; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.javafmlmod.FMLModContainer; @Mod(MUtilMod.MOD_ID) -@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) public class MUtilMod { public static final String MOD_ID = "mutil"; - public MUtilMod() { - ConfigHandler.setup(); + public MUtilMod(IEventBus modBus, ModContainer container, FMLModContainer modContainer, Dist side) { + ConfigHandler.setup(container, side); } @SubscribeEvent public static void clientSetup(FMLClientSetupEvent event) { - Perks.init(Minecraft.getInstance().getUser().getUuid()); + Perks.init(Minecraft.getInstance().getUser().getSessionId()); } } diff --git a/src/main/java/se/mickelus/mutil/data/DataStore.java b/src/main/java/se/mickelus/mutil/data/DataStore.java index b6d90c5..66a6d3f 100644 --- a/src/main/java/se/mickelus/mutil/data/DataStore.java +++ b/src/main/java/se/mickelus/mutil/data/DataStore.java @@ -1,7 +1,28 @@ package se.mickelus.mutil.data; +import java.io.IOException; +import java.io.Reader; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import com.google.common.collect.Maps; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import com.mojang.serialization.JsonOps; + import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.packs.resources.Resource; @@ -9,20 +30,12 @@ import net.minecraft.server.packs.resources.SimplePreparableReloadListener; import net.minecraft.util.GsonHelper; import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraftforge.common.crafting.CraftingHelper; -import net.minecraftforge.common.crafting.conditions.ICondition; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.forgespi.Environment; -import net.minecraftforge.forgespi.language.IModInfo; -import net.minecraftforge.server.ServerLifecycleHooks; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.io.IOException; -import java.io.Reader; -import java.util.*; -import java.util.stream.Collectors; +import net.neoforged.fml.ModList; +import net.neoforged.neoforge.common.conditions.ICondition; +import net.neoforged.neoforge.common.crafting.CraftingHelper; +import net.neoforged.neoforge.server.ServerLifecycleHooks; +import net.neoforged.neoforgespi.Environment; +import net.neoforged.neoforgespi.language.IModInfo; @ParametersAreNonnullByDefault public class DataStore extends SimplePreparableReloadListener> { @@ -62,7 +75,7 @@ protected Map prepare(ResourceManager resourceMan } String path = entry.getKey().getPath(); - ResourceLocation location = new ResourceLocation(entry.getKey().getNamespace(), path.substring(i, path.length() - jsonExtLength)); + ResourceLocation location = ResourceLocation.fromNamespaceAndPath(entry.getKey().getNamespace(), path.substring(i, path.length() - jsonExtLength)); try (Reader reader = entry.getValue().openAsReader()) { JsonElement json; @@ -175,8 +188,33 @@ protected boolean shouldLoad(JsonElement json) { } JsonObject jsonObject = json.getAsJsonObject(); - return !jsonObject.has("conditions") - || CraftingHelper.processConditions(GsonHelper.getAsJsonArray(jsonObject, "conditions"), ICondition.IContext.EMPTY); +// return !jsonObject.has("conditions") +// || CraftingHelper.processConditions(GsonHelper.getAsJsonArray(jsonObject, "conditions"), ICondition.IContext.EMPTY); + //TODO: dirty hack & untested, look here for NullPointerExceptions! + //WARN + return !jsonObject.has("conditions") || processConditions(GsonHelper.getAsJsonArray(jsonObject, "conditions"), ICondition.IContext.EMPTY); + } + public static boolean processConditions(JsonArray conditions, ICondition.IContext context) + { + for (int x = 0; x < conditions.size(); x++) + { + if (!conditions.get(x).isJsonObject()) + throw new JsonSyntaxException("Conditions must be an array of JsonObjects"); + + JsonObject json = conditions.get(x).getAsJsonObject(); + if (!getCondition(json).test(context)) + return false; + } + return true; + } + public static ICondition getCondition(JsonObject json) + { + ResourceLocation type = ResourceLocation.parse(GsonHelper.getAsString(json, "type")); +// IConditionSerializer serializer = conditions.get(type); +// if (serializer == null) +// throw new JsonSyntaxException("Unknown condition type: " + type.toString()); +// return serializer.read(json); + return ICondition.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); } protected void processData() { diff --git a/src/main/java/se/mickelus/mutil/data/MergingDataStore.java b/src/main/java/se/mickelus/mutil/data/MergingDataStore.java index 2fb13c0..679e3e4 100644 --- a/src/main/java/se/mickelus/mutil/data/MergingDataStore.java +++ b/src/main/java/se/mickelus/mutil/data/MergingDataStore.java @@ -42,7 +42,7 @@ protected Map prepare(ResourceManager resourceMan } String path = entry.getKey().getPath(); - ResourceLocation location = new ResourceLocation(entry.getKey().getNamespace(), path.substring(i, path.length() - jsonExtLength)); + ResourceLocation location = ResourceLocation.fromNamespaceAndPath(entry.getKey().getNamespace(), path.substring(i, path.length() - jsonExtLength)); JsonArray allResources = new JsonArray(); diff --git a/src/main/java/se/mickelus/mutil/data/deserializer/BlockDeserializer.java b/src/main/java/se/mickelus/mutil/data/deserializer/BlockDeserializer.java index 2a425d1..e095044 100644 --- a/src/main/java/se/mickelus/mutil/data/deserializer/BlockDeserializer.java +++ b/src/main/java/se/mickelus/mutil/data/deserializer/BlockDeserializer.java @@ -1,15 +1,17 @@ package se.mickelus.mutil.data.deserializer; +import java.lang.reflect.Type; + +import javax.annotation.ParametersAreNonnullByDefault; + import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; + +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; -import net.minecraftforge.registries.ForgeRegistries; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.lang.reflect.Type; @ParametersAreNonnullByDefault public class BlockDeserializer implements JsonDeserializer { @@ -17,9 +19,9 @@ public class BlockDeserializer implements JsonDeserializer { public Block deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { String string = json.getAsString(); if (string != null) { - ResourceLocation resourceLocation = new ResourceLocation(string); - if (ForgeRegistries.BLOCKS.containsKey(resourceLocation)) { - return ForgeRegistries.BLOCKS.getValue(resourceLocation); + ResourceLocation resourceLocation = ResourceLocation.parse(string); + if (BuiltInRegistries.BLOCK.containsKey(resourceLocation)) { + return BuiltInRegistries.BLOCK.get(resourceLocation); } } diff --git a/src/main/java/se/mickelus/mutil/data/deserializer/ItemDeserializer.java b/src/main/java/se/mickelus/mutil/data/deserializer/ItemDeserializer.java index c13ed2b..2e9394d 100644 --- a/src/main/java/se/mickelus/mutil/data/deserializer/ItemDeserializer.java +++ b/src/main/java/se/mickelus/mutil/data/deserializer/ItemDeserializer.java @@ -1,15 +1,17 @@ package se.mickelus.mutil.data.deserializer; +import java.lang.reflect.Type; + +import javax.annotation.ParametersAreNonnullByDefault; + import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; + +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; -import net.minecraftforge.registries.ForgeRegistries; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.lang.reflect.Type; @ParametersAreNonnullByDefault public class ItemDeserializer implements JsonDeserializer { @@ -17,9 +19,9 @@ public class ItemDeserializer implements JsonDeserializer { public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { String string = json.getAsString(); if (string != null) { - ResourceLocation resourceLocation = new ResourceLocation(string); - if (ForgeRegistries.ITEMS.containsKey(resourceLocation)) { - return ForgeRegistries.ITEMS.getValue(resourceLocation); + ResourceLocation resourceLocation = ResourceLocation.parse(string); + if (BuiltInRegistries.ITEM.containsKey(resourceLocation)) { + return BuiltInRegistries.ITEM.get(resourceLocation); } } diff --git a/src/main/java/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java b/src/main/java/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java index c10776d..a637849 100644 --- a/src/main/java/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java +++ b/src/main/java/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java @@ -13,7 +13,7 @@ public class ResourceLocationDeserializer implements JsonDeserializer { public static ResourceLocation deserialize(JsonElement json) throws JsonParseException { - return new ResourceLocation(json.getAsString()); + return ResourceLocation.parse(json.getAsString()); } @Override diff --git a/src/main/java/se/mickelus/mutil/effect/EffectTooltipRenderer.java b/src/main/java/se/mickelus/mutil/effect/EffectTooltipRenderer.java index 1f99754..d96e090 100644 --- a/src/main/java/se/mickelus/mutil/effect/EffectTooltipRenderer.java +++ b/src/main/java/se/mickelus/mutil/effect/EffectTooltipRenderer.java @@ -1,18 +1,18 @@ package se.mickelus.mutil.effect; +import java.util.function.Function; +import java.util.function.Supplier; + import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.vertex.PoseStack; + import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.inventory.EffectRenderingInventoryScreen; import net.minecraft.network.chat.Component; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.client.extensions.common.IClientMobEffectExtensions; - -import java.util.function.Function; -import java.util.function.Supplier; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.client.extensions.common.IClientMobEffectExtensions; public class EffectTooltipRenderer implements IClientMobEffectExtensions { private final Function constructEffectTooltip; diff --git a/src/main/java/se/mickelus/mutil/gui/ColorHelper.java b/src/main/java/se/mickelus/mutil/gui/ColorHelper.java index 07457a1..0402f85 100644 --- a/src/main/java/se/mickelus/mutil/gui/ColorHelper.java +++ b/src/main/java/se/mickelus/mutil/gui/ColorHelper.java @@ -1,8 +1,8 @@ package se.mickelus.mutil.gui; -import net.minecraft.util.Mth; +import java.awt.Color; -import java.awt.*; +import net.minecraft.util.Mth; public class ColorHelper { private static int withSaturation(int color, double saturation) { diff --git a/src/main/java/se/mickelus/mutil/gui/GuiItem.java b/src/main/java/se/mickelus/mutil/gui/GuiItem.java index 05d3354..4fdb881 100644 --- a/src/main/java/se/mickelus/mutil/gui/GuiItem.java +++ b/src/main/java/se/mickelus/mutil/gui/GuiItem.java @@ -7,6 +7,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.Item.TooltipContext; import java.util.ArrayList; import java.util.List; @@ -109,7 +110,7 @@ protected String getCountString() { @Override public List getTooltipLines() { if (showTooltip && itemStack != null && hasFocus()) { - return new ArrayList<>(itemStack.getTooltipLines(Minecraft.getInstance().player, + return new ArrayList<>(itemStack.getTooltipLines(TooltipContext.EMPTY, Minecraft.getInstance().player, mc.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL)); } diff --git a/src/main/java/se/mickelus/mutil/gui/GuiTexture.java b/src/main/java/se/mickelus/mutil/gui/GuiTexture.java index ff8741f..ce90fbc 100644 --- a/src/main/java/se/mickelus/mutil/gui/GuiTexture.java +++ b/src/main/java/se/mickelus/mutil/gui/GuiTexture.java @@ -1,6 +1,5 @@ package se.mickelus.mutil.gui; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/se/mickelus/mutil/gui/ToggleableSlot.java b/src/main/java/se/mickelus/mutil/gui/ToggleableSlot.java index 9ba4b4f..7202524 100644 --- a/src/main/java/se/mickelus/mutil/gui/ToggleableSlot.java +++ b/src/main/java/se/mickelus/mutil/gui/ToggleableSlot.java @@ -2,9 +2,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.SlotItemHandler; - +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.SlotItemHandler; import javax.annotation.Nullable; public class ToggleableSlot extends SlotItemHandler { diff --git a/src/main/java/se/mickelus/mutil/gui/hud/GuiRootHud.java b/src/main/java/se/mickelus/mutil/gui/hud/GuiRootHud.java index c95fcfe..19a5c7e 100644 --- a/src/main/java/se/mickelus/mutil/gui/hud/GuiRootHud.java +++ b/src/main/java/se/mickelus/mutil/gui/hud/GuiRootHud.java @@ -1,14 +1,14 @@ package se.mickelus.mutil.gui.hud; -import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; + import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.phys.AABB; -import net.minecraft.core.BlockPos; import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; import se.mickelus.mutil.gui.GuiElement; import se.mickelus.mutil.gui.animation.KeyframeAnimation; diff --git a/src/main/java/se/mickelus/mutil/network/AbstractPacket.java b/src/main/java/se/mickelus/mutil/network/AbstractPacket.java index e4196ec..686b1f6 100644 --- a/src/main/java/se/mickelus/mutil/network/AbstractPacket.java +++ b/src/main/java/se/mickelus/mutil/network/AbstractPacket.java @@ -1,17 +1,17 @@ package se.mickelus.mutil.network; +import java.io.IOException; + import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.world.entity.player.Player; -import java.io.IOException; - /** * AbstractPacket class. Should be the parent of all packets wishing to use the PacketHandler. * @author sirgingalot, mickelus */ -public abstract class AbstractPacket { - +public abstract class AbstractPacket implements CustomPacketPayload { /** * Encode the packet data into the ByteBuf stream. Complex data sets may need specific data handlers (See @link{cpw.mods.fml.common.network.ByteBuffUtils}) * diff --git a/src/main/java/se/mickelus/mutil/network/PacketHandler.java b/src/main/java/se/mickelus/mutil/network/PacketHandler.java index 0903587..9f69d52 100644 --- a/src/main/java/se/mickelus/mutil/network/PacketHandler.java +++ b/src/main/java/se/mickelus/mutil/network/PacketHandler.java @@ -1,107 +1,121 @@ package se.mickelus.mutil.network; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceKey; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.network.NetworkDirection; -import net.minecraftforge.network.NetworkEvent; -import net.minecraftforge.network.NetworkRegistry; -import net.minecraftforge.network.PacketDistributor; -import net.minecraftforge.network.simple.SimpleChannel; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; -import javax.annotation.ParametersAreNonnullByDefault; -import java.util.ArrayList; -import java.util.function.Supplier; - -@ParametersAreNonnullByDefault public class PacketHandler { - private static final Logger logger = LogManager.getLogger(); - - private final SimpleChannel channel; - private final ArrayList> packets = new ArrayList<>(); - - public PacketHandler(String namespace, String channelId, String protocolVersion) { - channel = NetworkRegistry.newSimpleChannel( - new ResourceLocation(namespace, channelId), - () -> protocolVersion, - protocolVersion::equals, - protocolVersion::equals); - } - - /** - * Register your packet with the pipeline. Discriminators are automatically set. - * - * @param packetClass the class to register - * @param supplier A supplier returning an object instance of packetClass - * - * @return whether registration was successful. Failure may occur if 256 packets have been registered or if the registry already contains this packet - */ - public boolean registerPacket(Class packetClass, Supplier supplier) { - if (packets.size() > 256) { - logger.warn("Attempted to register packet but packet list is full: " + packetClass.toString()); - return false; - } - - if (packets.contains(packetClass)) { - logger.warn("Attempted to register packet but packet is already in list: " + packetClass.toString()); - return false; - } - - channel.messageBuilder(packetClass, packets.size()) - .encoder(AbstractPacket::toBytes) - .decoder(buffer -> { - T packet = supplier.get(); - packet.fromBytes(buffer); - return packet; - }) - .consumerNetworkThread(this::onMessage) - .add(); - - packets.add(packetClass); - return true; - } - - public void onMessage(AbstractPacket message, Supplier ctx) { - ctx.get().enqueueWork(() -> { - if (ctx.get().getDirection().getReceptionSide().isServer()) { - message.handle(ctx.get().getSender()); - } else { - message.handle(getClientPlayer()); - } - }); - ctx.get().setPacketHandled(true); - } - - @OnlyIn(Dist.CLIENT) - private Player getClientPlayer() { - return Minecraft.getInstance().player; - } - - public void sendTo(AbstractPacket message, ServerPlayer player) { - channel.sendTo(message, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); - } - - public void sendToAllPlayers(AbstractPacket message) { - channel.send(PacketDistributor.ALL.noArg(), message); - } - - public void sendToAllPlayersNear(AbstractPacket message, BlockPos pos, double r2, ResourceKey dim) { - channel.send(PacketDistributor.NEAR.with(PacketDistributor.TargetPoint.p(pos.getX(), pos.getY(), pos.getZ(), r2, dim)), message); - } - - @OnlyIn(Dist.CLIENT) - public void sendToServer(AbstractPacket message) { - // crashes sometimes happen due to the connection being null - if (Minecraft.getInstance().getConnection() != null) { - channel.sendToServer(message); - } - } -} \ No newline at end of file + private static final Logger logger = LogManager.getLogger(); + + private String modid; + private String version; + + private @Nullable PayloadRegistrar registrar = null; + + public PacketHandler(String modid, String version) { + this.modid = modid; + this.version = version; + } + + public void beginRegistration(final RegisterPayloadHandlersEvent event) { + this.registrar = event.registrar(modid).versioned(version).optional(); + } + + public void endRegistration() { + this.registrar = null; + } + + public void onMessage(T message, IPayloadContext ctx) { +// ctx.enqueueWork(() -> { +// if (ctx.getDirection().getReceptionSide().isServer()) { +// message.handle(ctx.getSender()); +// } else { +// message.handle(getClientPlayer()); +// } +// }); +// ctx.setPacketHandled(true); + ctx.enqueueWork(() -> { + if (ctx.flow().getReceptionSide().isServer()) { + message.handle(ctx.player()); + } else { + message.handle(getClientPlayer()); + } + }); + } + + /** + * Register your packet with the pipeline. Discriminators are automatically set. + *

+ * Must be called in the context of + * {@link net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent}. + *
+ * Requires {@link PacketHandler#beginRegistration} to be called beforehand. + * + * @param type the packet's type. + * @param codec the packet's {@link net.minecraft.network.codec.StreamCodec}. + * @param supplier A supplier returning an object instance of the packet. + * + * @return whether registration was successful. Failure may occur if 256 packets + * have been registered or if the registry already contains this packet + */ + public boolean registerPacket(CustomPacketPayload.Type type, + StreamCodec codec, Supplier packet) { + if (this.registrar == null) { + logger.warn("Attempted to register packet outside registration event: " + type.id().toString()); + return false; + } else { + try { + this.registrar.playBidirectional(type, codec, (payload, context) -> { + if (payload instanceof AbstractPacket) { + this.onMessage((AbstractPacket)payload, context); + } + }); + } catch(UnsupportedOperationException e) { + logger.warn("Error while registering packet \""+type.id()+"\": "+e.getMessage()); + return false; + } + } + return true; + } + + @OnlyIn(Dist.CLIENT) + private Player getClientPlayer() { + return Minecraft.getInstance().player; + } + + public void sendTo(AbstractPacket message, ServerPlayer player) { + PacketDistributor.sendToPlayer(player, message); + } + + public void sendToAllPlayers(AbstractPacket message) { + PacketDistributor.sendToAllPlayers(message); + } + + public void sendToAllPlayersNear(AbstractPacket message, BlockPos pos, double r2, ServerLevel dim) { + PacketDistributor.sendToPlayersNear(dim, null, pos.getX(), pos.getY(), pos.getZ(), r2, message); + } + + @OnlyIn(Dist.CLIENT) + public void sendToServer(AbstractPacket message) { + PacketDistributor.sendToServer(message); + } +} diff --git a/src/main/java/se/mickelus/mutil/scheduling/AbstractScheduler.java b/src/main/java/se/mickelus/mutil/scheduling/AbstractScheduler.java index 53eb3ac..6ec744d 100644 --- a/src/main/java/se/mickelus/mutil/scheduling/AbstractScheduler.java +++ b/src/main/java/se/mickelus/mutil/scheduling/AbstractScheduler.java @@ -1,13 +1,15 @@ package se.mickelus.mutil.scheduling; -import com.google.common.collect.Queues; -import net.minecraft.server.TickTask; -import net.minecraftforge.event.TickEvent; - -import javax.annotation.ParametersAreNonnullByDefault; import java.util.Iterator; import java.util.Queue; +import javax.annotation.ParametersAreNonnullByDefault; + +import com.google.common.collect.Queues; + +import net.minecraft.server.TickTask; +import net.neoforged.bus.api.Event; + @ParametersAreNonnullByDefault public class AbstractScheduler { private final Queue queue = Queues.newConcurrentLinkedQueue(); @@ -22,10 +24,7 @@ public void schedule(String id, int delay, Runnable task) { queue.add(new Task(id, counter + delay, task)); } - public void tick(TickEvent event) { - if (event.phase != TickEvent.Phase.END) { - return; - } + public void tick(Event event) { for (Iterator it = queue.iterator(); it.hasNext(); ) { Task task = it.next(); diff --git a/src/main/java/se/mickelus/mutil/scheduling/ClientScheduler.java b/src/main/java/se/mickelus/mutil/scheduling/ClientScheduler.java index 2f756ee..f709c15 100644 --- a/src/main/java/se/mickelus/mutil/scheduling/ClientScheduler.java +++ b/src/main/java/se/mickelus/mutil/scheduling/ClientScheduler.java @@ -1,14 +1,14 @@ package se.mickelus.mutil.scheduling; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; - import javax.annotation.ParametersAreNonnullByDefault; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.ClientTickEvent; + @ParametersAreNonnullByDefault public class ClientScheduler extends AbstractScheduler { @SubscribeEvent - public void onClientTick(TickEvent.ClientTickEvent event) { + public void onClientTick(ClientTickEvent.Post event) { this.tick(event); } diff --git a/src/main/java/se/mickelus/mutil/scheduling/ServerScheduler.java b/src/main/java/se/mickelus/mutil/scheduling/ServerScheduler.java index 6b6343f..8b3aaf4 100644 --- a/src/main/java/se/mickelus/mutil/scheduling/ServerScheduler.java +++ b/src/main/java/se/mickelus/mutil/scheduling/ServerScheduler.java @@ -1,14 +1,14 @@ package se.mickelus.mutil.scheduling; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; - import javax.annotation.ParametersAreNonnullByDefault; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.event.tick.ServerTickEvent; + @ParametersAreNonnullByDefault public class ServerScheduler extends AbstractScheduler { @SubscribeEvent - public void onServerTick(TickEvent.ServerTickEvent event) { - tick(event); + public void onServerTick(ServerTickEvent.Post event) { + this.tick(event); } } diff --git a/src/main/java/se/mickelus/mutil/util/ItemHandlerWrapper.java b/src/main/java/se/mickelus/mutil/util/ItemHandlerWrapper.java index f78045d..8c411bb 100644 --- a/src/main/java/se/mickelus/mutil/util/ItemHandlerWrapper.java +++ b/src/main/java/se/mickelus/mutil/util/ItemHandlerWrapper.java @@ -3,8 +3,7 @@ import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.IItemHandler; - +import net.neoforged.neoforge.items.IItemHandler; import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault diff --git a/src/main/java_old/se/mickelus/mutil/ConfigHandler.java b/src/main/java_old/se/mickelus/mutil/ConfigHandler.java new file mode 100644 index 0000000..9413b41 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/ConfigHandler.java @@ -0,0 +1,47 @@ +package se.mickelus.mutil; + + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLEnvironment; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +class ConfigHandler { + public static Client client; + static ForgeConfigSpec clientSpec; + + public static void setup() { + if (FMLEnvironment.dist.isClient()) { + setupClient(); + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, clientSpec); + FMLJavaModLoadingContext.get().getModEventBus().register(ConfigHandler.client); + } + } + + @OnlyIn(Dist.CLIENT) + private static void setupClient() { + final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); + clientSpec = specPair.getRight(); + client = specPair.getLeft(); + } + + @OnlyIn(Dist.CLIENT) + public static class Client { + public ForgeConfigSpec.BooleanValue queryPerks; + + Client(ForgeConfigSpec.Builder builder) { + queryPerks = builder + .comment("Controls if perks data should be queried on startup") + .define("query_perks", true); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/MUtilMod.java b/src/main/java_old/se/mickelus/mutil/MUtilMod.java new file mode 100644 index 0000000..78b348b --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/MUtilMod.java @@ -0,0 +1,21 @@ +package se.mickelus.mutil; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; + +@Mod(MUtilMod.MOD_ID) +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public class MUtilMod { + public static final String MOD_ID = "mutil"; + + public MUtilMod() { + ConfigHandler.setup(); + } + + @SubscribeEvent + public static void clientSetup(FMLClientSetupEvent event) { + Perks.init(Minecraft.getInstance().getUser().getUuid()); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/Perks.java b/src/main/java_old/se/mickelus/mutil/Perks.java new file mode 100644 index 0000000..f772c6f --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/Perks.java @@ -0,0 +1,66 @@ +package se.mickelus.mutil; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.concurrent.ExecutionException; + +public class Perks { + private static final Logger logger = LogManager.getLogger(); + private static volatile Data data; + + public static void init(String uuid) { + if (!ConfigHandler.client.queryPerks.get()) { + logger.info("Perks query disabled, skipping fetch!"); + data = new Data(); + return; + } + try { + Gson gson = new GsonBuilder().create(); + HttpRequest request = HttpRequest.newBuilder(new URI("https://mickelus.se/util/perks/" + uuid.replace("-", ""))) + .header("Accept", "application/json") + .build(); + HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenApply(body -> gson.fromJson(body, Data.class)) + .thenAccept(Perks::setData) + .get(); + } catch (URISyntaxException | ExecutionException | InterruptedException e) { + logger.warn("Failed to get perk data: " + e.getMessage()); + data = new Data(); + } + } + + public static synchronized Data getData() { + return data; + } + + private static synchronized void setData(Data newData) { + data = newData; + } + + public static class Data { + public int support; + public int contribute; + public int community; + public int moderate; + + @Override + public String toString() { + return "PerkData{" + + "support=" + support + + ", contribute=" + contribute + + ", community=" + community + + ", moderate=" + moderate + + '}'; + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/data/AbstractUpdateDataPacket.java b/src/main/java_old/se/mickelus/mutil/data/AbstractUpdateDataPacket.java new file mode 100644 index 0000000..e89447b --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/AbstractUpdateDataPacket.java @@ -0,0 +1,52 @@ +package se.mickelus.mutil.data; + +import com.google.gson.JsonElement; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import se.mickelus.mutil.network.AbstractPacket; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +@ParametersAreNonnullByDefault +public abstract class AbstractUpdateDataPacket extends AbstractPacket { + protected String directory; + protected Map data; + + public AbstractUpdateDataPacket() {} + + public AbstractUpdateDataPacket(String directory, Map data) { + this.directory = directory; + this.data = data.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().toString() + )); + } + + @Override + public void toBytes(FriendlyByteBuf buffer) { + buffer.writeUtf(directory); + buffer.writeInt(data.size()); + data.forEach((resourceLocation, data) -> { + buffer.writeResourceLocation(resourceLocation); + buffer.writeUtf(data); + }); + } + + @Override + public void fromBytes(FriendlyByteBuf buffer) { + directory = buffer.readUtf(); + int count = buffer.readInt(); + data = new HashMap<>(); + for (int i = 0; i < count; i++) { + data.put(buffer.readResourceLocation(), buffer.readUtf()); + } + } + + @Override + public abstract void handle(Player player); +} diff --git a/src/main/java_old/se/mickelus/mutil/data/DataDistributor.java b/src/main/java_old/se/mickelus/mutil/data/DataDistributor.java new file mode 100644 index 0000000..812c375 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/DataDistributor.java @@ -0,0 +1,12 @@ +package se.mickelus.mutil.data; + +import com.google.gson.JsonElement; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; + +import java.util.Map; + +public interface DataDistributor { + public void sendToAll(String directory, Map dataMap); + public void sendToPlayer(ServerPlayer player, String directory, Map dataMap); +} diff --git a/src/main/java_old/se/mickelus/mutil/data/DataStore.java b/src/main/java_old/se/mickelus/mutil/data/DataStore.java new file mode 100644 index 0000000..b6d90c5 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/DataStore.java @@ -0,0 +1,233 @@ +package se.mickelus.mutil.data; + +import com.google.common.collect.Maps; +import com.google.gson.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraftforge.common.crafting.CraftingHelper; +import net.minecraftforge.common.crafting.conditions.ICondition; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.forgespi.Environment; +import net.minecraftforge.forgespi.language.IModInfo; +import net.minecraftforge.server.ServerLifecycleHooks; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.io.IOException; +import java.io.Reader; +import java.util.*; +import java.util.stream.Collectors; + +@ParametersAreNonnullByDefault +public class DataStore extends SimplePreparableReloadListener> { + protected static final int jsonExtLength = ".json".length(); + private static final Logger logger = LogManager.getLogger(); + protected Gson gson; + protected String namespace; + protected String directory; + protected Class dataClass; + protected Map rawData; + protected Map dataMap; + protected List listeners; + private DataDistributor syncronizer; + + public DataStore(Gson gson, String namespace, String directory, Class dataClass, DataDistributor synchronizer) { + this.gson = gson; + this.namespace = namespace; + this.directory = directory; + + this.dataClass = dataClass; + this.syncronizer = synchronizer; + + rawData = Collections.emptyMap(); + dataMap = Collections.emptyMap(); + + listeners = new LinkedList<>(); + } + + protected Map prepare(ResourceManager resourceManager, ProfilerFiller profiler) { + logger.debug("Reading data for {} data store...", directory); + Map map = Maps.newHashMap(); + int i = this.directory.length() + 1; + + for (Map.Entry entry : resourceManager.listResources(directory, rl -> rl.getPath().endsWith(".json")).entrySet()) { + if (!namespace.equals(entry.getKey().getNamespace())) { + continue; + } + + String path = entry.getKey().getPath(); + ResourceLocation location = new ResourceLocation(entry.getKey().getNamespace(), path.substring(i, path.length() - jsonExtLength)); + + try (Reader reader = entry.getValue().openAsReader()) { + JsonElement json; + + if (dataClass.isArray()) { + JsonArray sources = getSources(entry.getValue()); + json = GsonHelper.fromJson(gson, reader, JsonArray.class); + json.getAsJsonArray().forEach(element -> { + if (element.isJsonObject()) { + element.getAsJsonObject().add("sources", sources); + } + }); + } else { + json = GsonHelper.fromJson(gson, reader, JsonElement.class); + json.getAsJsonObject().add("sources", getSources(entry.getValue())); + } + + if (json != null) { + if (shouldLoad(json)) { + JsonElement duplicate = map.put(location, json); + if (duplicate != null) { + throw new IllegalStateException("Duplicate data ignored with ID " + location); + } + } else { + logger.debug("Skipping data '{}' due to condition", entry.getKey()); + } + } else { + logger.error("Couldn't load data from '{}' as it's null or empty", entry.getKey()); + } + } catch (IllegalArgumentException | IOException | JsonParseException exception) { + logger.error("Couldn't parse data '{}' from '{}'", location, entry.getKey(), exception); + } + } + + return map; + } + + protected JsonArray getSources(Resource resource) { + String fileId = resource.sourcePackId(); + JsonArray result = new JsonArray(); + + ModList.get().getModFiles().stream() + .filter(modInfo -> fileId.equals(modInfo.getFile().getFileName())) + .flatMap(fileInfo -> fileInfo.getMods().stream()) + .map(IModInfo::getDisplayName) + .forEach(result::add); + + if (result.size() == 0) { + result.add(fileId); + } + + return result; + } + + @Override + protected void apply(Map splashList, ResourceManager resourceManager, ProfilerFiller profiler) { + rawData = splashList; + + // PacketHandler dependencies get upset when called upon before the server has started properly + if (Environment.get().getDist().isDedicatedServer() && ServerLifecycleHooks.getCurrentServer() != null) { + syncronizer.sendToAll(directory, rawData); + } + + parseData(rawData); + } + + public void sendToPlayer(ServerPlayer player) { + syncronizer.sendToPlayer(player, directory, rawData); + } + + public void loadFromPacket(Map data) { + Map splashList = data.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> { + if (dataClass.isArray()) { + return GsonHelper.fromJson(gson, entry.getValue(), JsonArray.class); + } else { + return GsonHelper.fromJson(gson, entry.getValue(), JsonElement.class); + } + } + )); + + parseData(splashList); + } + + public void parseData(Map splashList) { + logger.info("Loaded {} {}", String.format("%3d", splashList.values().size()), directory); + dataMap = splashList.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> gson.fromJson(entry.getValue(), dataClass) + )); + + processData(); + + listeners.forEach(Runnable::run); + } + + protected boolean shouldLoad(JsonElement json) { + if (json.isJsonArray()) { + JsonArray arr = json.getAsJsonArray(); + if (arr.size() > 0) { + json = arr.get(0); + } + } + + if (!json.isJsonObject()) { + return true; + } + + JsonObject jsonObject = json.getAsJsonObject(); + return !jsonObject.has("conditions") + || CraftingHelper.processConditions(GsonHelper.getAsJsonArray(jsonObject, "conditions"), ICondition.IContext.EMPTY); + } + + protected void processData() { + + } + + public Map getRawData() { + return rawData; + } + + public String getDirectory() { + return directory; + } + + /** + * Get the resource at the given location from the set of resources that this listener is managing + * + * @param resourceLocation A resource location + * @return An object matching the type of this listener, or null if none exists at the given location + */ + public V getData(ResourceLocation resourceLocation) { + return dataMap.get(resourceLocation); + } + + /** + * @return all data from this store. + */ + public Map getData() { + return dataMap; + } + + /** + * Get all resources (if any) that are within the directory denoted by the provided resource location + * + * @param resourceLocation + * @return + */ + public Collection getDataIn(ResourceLocation resourceLocation) { + return getData().entrySet().stream() + .filter(entry -> resourceLocation.getNamespace().equals(entry.getKey().getNamespace()) + && entry.getKey().getPath().startsWith(resourceLocation.getPath())) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } + + /** + * Listen to changes on resources in this store + * + * @param callback A runnable that is to be called when the store is reloaded + */ + public void onReload(Runnable callback) { + listeners.add(callback); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/data/MergingDataStore.java b/src/main/java_old/se/mickelus/mutil/data/MergingDataStore.java new file mode 100644 index 0000000..2fb13c0 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/MergingDataStore.java @@ -0,0 +1,102 @@ +package se.mickelus.mutil.data; + +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.profiling.ProfilerFiller; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.Reader; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public abstract class MergingDataStore extends DataStore { + private static final Logger logger = LogManager.getLogger(); + + protected Class arrayClass; + + public MergingDataStore(Gson gson, String namespace, String directory, Class entryClass, Class arrayClass, DataDistributor synchronizer) { + super(gson, namespace, directory, entryClass, synchronizer); + + this.arrayClass = arrayClass; + } + + @Override + protected Map prepare(ResourceManager resourceManager, ProfilerFiller profiler) { + logger.debug("Reading data for {} data store...", directory); + Map map = Maps.newHashMap(); + int i = this.directory.length() + 1; + + for (Map.Entry> entry : resourceManager.listResourceStacks(directory, rl -> rl.getPath().endsWith(".json")).entrySet()) { + if (!namespace.equals(entry.getKey().getNamespace())) { + continue; + } + + String path = entry.getKey().getPath(); + ResourceLocation location = new ResourceLocation(entry.getKey().getNamespace(), path.substring(i, path.length() - jsonExtLength)); + + JsonArray allResources = new JsonArray(); + + for (Resource resource : entry.getValue()) { + try (Reader reader = resource.openAsReader()) { + JsonObject json = GsonHelper.fromJson(gson, reader, JsonObject.class); + json.add("sources", getSources(resource)); + + if (json != null) { + if (shouldLoad(json)) { + allResources.add(json); + } else { + logger.debug("Skipping data '{}' from '{}' due to condition", entry.getKey(), resource.sourcePackId()); + } + } else { + logger.error("Couldn't load data from '{}' in data pack '{}' as it's empty or null", + entry.getKey(), resource.sourcePackId()); + } + } catch (RuntimeException | IOException e) { + logger.error("Couldn't load data from '{}' in data pack '{}'", entry.getKey(), resource.sourcePackId(), e); + } + } + + if (allResources.size() > 0) { + map.put(location, allResources); + } + } + + return map; + } + + @Override + public void loadFromPacket(Map data) { + Map splashList = data.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> GsonHelper.fromJson(gson, entry.getValue(), JsonArray.class) + )); + + parseData(splashList); + } + + public void parseData(Map splashList) { + logger.info("Loaded {} {}", String.format("%3d", splashList.values().size()), directory); + dataMap = splashList.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> mergeData(gson.fromJson(entry.getValue(), arrayClass)) + )); + + processData(); + + listeners.forEach(Runnable::run); + } + + protected abstract V mergeData(U collection); +} diff --git a/src/main/java_old/se/mickelus/mutil/data/deserializer/BlockDeserializer.java b/src/main/java_old/se/mickelus/mutil/data/deserializer/BlockDeserializer.java new file mode 100644 index 0000000..2a425d1 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/deserializer/BlockDeserializer.java @@ -0,0 +1,28 @@ +package se.mickelus.mutil.data.deserializer; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraftforge.registries.ForgeRegistries; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.lang.reflect.Type; + +@ParametersAreNonnullByDefault +public class BlockDeserializer implements JsonDeserializer { + @Override + public Block deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + String string = json.getAsString(); + if (string != null) { + ResourceLocation resourceLocation = new ResourceLocation(string); + if (ForgeRegistries.BLOCKS.containsKey(resourceLocation)) { + return ForgeRegistries.BLOCKS.getValue(resourceLocation); + } + } + + return null; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/data/deserializer/BlockPosDeserializer.java b/src/main/java_old/se/mickelus/mutil/data/deserializer/BlockPosDeserializer.java new file mode 100644 index 0000000..50bedca --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/deserializer/BlockPosDeserializer.java @@ -0,0 +1,16 @@ +package se.mickelus.mutil.data.deserializer; + +import com.google.gson.*; +import net.minecraft.core.BlockPos; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.lang.reflect.Type; + +@ParametersAreNonnullByDefault +public class BlockPosDeserializer implements JsonDeserializer { + @Override + public BlockPos deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray array = json.getAsJsonArray(); + return new BlockPos(array.get(0).getAsInt(), array.get(1).getAsInt(), array.get(2).getAsInt()); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/data/deserializer/ItemDeserializer.java b/src/main/java_old/se/mickelus/mutil/data/deserializer/ItemDeserializer.java new file mode 100644 index 0000000..c13ed2b --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/deserializer/ItemDeserializer.java @@ -0,0 +1,28 @@ +package se.mickelus.mutil.data.deserializer; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraftforge.registries.ForgeRegistries; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.lang.reflect.Type; + +@ParametersAreNonnullByDefault +public class ItemDeserializer implements JsonDeserializer { + @Override + public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + String string = json.getAsString(); + if (string != null) { + ResourceLocation resourceLocation = new ResourceLocation(string); + if (ForgeRegistries.ITEMS.containsKey(resourceLocation)) { + return ForgeRegistries.ITEMS.getValue(resourceLocation); + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java_old/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java b/src/main/java_old/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java new file mode 100644 index 0000000..c10776d --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/data/deserializer/ResourceLocationDeserializer.java @@ -0,0 +1,23 @@ +package se.mickelus.mutil.data.deserializer; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import net.minecraft.resources.ResourceLocation; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.lang.reflect.Type; + +@ParametersAreNonnullByDefault +public class ResourceLocationDeserializer implements JsonDeserializer { + + public static ResourceLocation deserialize(JsonElement json) throws JsonParseException { + return new ResourceLocation(json.getAsString()); + } + + @Override + public ResourceLocation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return deserialize(json); + } +} \ No newline at end of file diff --git a/src/main/java_old/se/mickelus/mutil/effect/EffectTooltipRenderer.java b/src/main/java_old/se/mickelus/mutil/effect/EffectTooltipRenderer.java new file mode 100644 index 0000000..1f99754 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/effect/EffectTooltipRenderer.java @@ -0,0 +1,45 @@ +package se.mickelus.mutil.effect; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.EffectRenderingInventoryScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.extensions.common.IClientMobEffectExtensions; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class EffectTooltipRenderer implements IClientMobEffectExtensions { + private final Function constructEffectTooltip; + + public EffectTooltipRenderer(Function constructEffectTooltip) { + this.constructEffectTooltip = constructEffectTooltip; + } + + @OnlyIn(Dist.CLIENT) + public static void renderInventoryEffectTooltip(GuiGraphics graphics, int x, int y, Supplier tooltip) { + Minecraft mc = Minecraft.getInstance(); + Window window = mc.getWindow(); + + int width = window.getGuiScaledWidth(); + int height = window.getGuiScaledHeight(); + int mouseX = (int) (mc.mouseHandler.xpos() * width / window.getScreenWidth()); + int mouseY = (int) (mc.mouseHandler.ypos() * height / window.getScreenHeight()); + + if (x < mouseX && mouseX < x + 120 && y < mouseY && mouseY < y + 32) { + graphics.renderTooltip(mc.font, tooltip.get(), mouseX, mouseY); + } + } + + @Override + public boolean renderInventoryIcon(final MobEffectInstance instance, final EffectRenderingInventoryScreen screen, + final GuiGraphics graphics, final int x, final int y, final int blitOffset) { + renderInventoryEffectTooltip(graphics, x, y, () -> Component.literal(constructEffectTooltip.apply(instance))); + return false; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/ClipRectGui.java b/src/main/java_old/se/mickelus/mutil/gui/ClipRectGui.java new file mode 100644 index 0000000..eddbdd0 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/ClipRectGui.java @@ -0,0 +1,44 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; + +public class ClipRectGui extends GuiElement { + public ClipRectGui(int x, int y, int width, int height) { + super(x, y, width, height); + } + + @Override + protected void drawChildren(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, + float opacity) { + graphics.enableScissor(refX, refY, refX + width, refY + height); + super.drawChildren(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + graphics.disableScissor(); + } + + @Override + public void updateFocusState(int refX, int refY, int mouseX, int mouseY) { + boolean gainFocus = mouseX >= getX() + refX + && mouseX < getX() + refX + getWidth() + && mouseY >= getY() + refY + && mouseY < getY() + refY + getHeight(); + + if (gainFocus != hasFocus) { + hasFocus = gainFocus; + if (hasFocus) { + onFocus(); + } + else { + onBlur(); + } + } + + if (hasFocus) { + elements.stream() + .filter(GuiElement::isVisible) + .forEach(element -> element.updateFocusState( + refX + x + getXOffset(this, element.attachmentAnchor) - getXOffset(element, element.attachmentPoint), + refY + y + getYOffset(this, element.attachmentAnchor) - getYOffset(element, element.attachmentPoint), + mouseX, mouseY)); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/ColorHelper.java b/src/main/java_old/se/mickelus/mutil/gui/ColorHelper.java new file mode 100644 index 0000000..07457a1 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/ColorHelper.java @@ -0,0 +1,51 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.util.Mth; + +import java.awt.*; + +public class ColorHelper { + private static int withSaturation(int color, double saturation) { + float[] hsl = new float[3]; + Color.RGBtoHSB((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsl); + return Color.HSBtoRGB(hsl[0], (float) saturation, hsl[2]); + } + + public static int multiplyBrightness(int color, double multiplier) { + float[] hsl = new float[3]; + Color.RGBtoHSB((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsl); + return Color.HSBtoRGB(hsl[0], hsl[1], Mth.clamp((float) multiplier * hsl[2], 0, 1)); + } + + public static int withBrightness(int color, double brightness) { + float[] hsl = new float[3]; + Color.RGBtoHSB((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsl); + return Color.HSBtoRGB(hsl[0], hsl[1], (float) brightness); + } + + public static int withAlpha(int color, double alpha) { + return ((int) Math.round(alpha * 255) << 24) | ((color) & 0x00FFFFFF); + } + + public static int blend(int colorA, int colorB, float ratio) { + int a1 = (colorA >> 24 & 0xff); + int r1 = ((colorA & 0xff0000) >> 16); + int g1 = ((colorA & 0xff00) >> 8); + int b1 = (colorA & 0xff); + + int a2 = (colorB >> 24 & 0xff); + int r2 = ((colorB & 0xff0000) >> 16); + int g2 = ((colorB & 0xff00) >> 8); + int b2 = (colorB & 0xff); + + int a = (int) ((a1 * (1 - ratio)) + (a2 * ratio)); + int r = (int) ((r1 * (1 - ratio)) + (r2 * ratio)); + int g = (int) ((g1 * (1 - ratio)) + (g2 * ratio)); + int b = (int) ((b1 * (1 - ratio)) + (b2 * ratio)); + + return a << 24 + | r << 16 + | g << 8 + | b; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/DisabledSlot.java b/src/main/java_old/se/mickelus/mutil/gui/DisabledSlot.java new file mode 100644 index 0000000..a2cc1c8 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/DisabledSlot.java @@ -0,0 +1,24 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.Nullable; + +public class DisabledSlot extends Slot { + public DisabledSlot(Container inventoryIn, int index, int xPosition, int yPosition) { + super(inventoryIn, index, xPosition, yPosition); + } + + @Override + public boolean mayPickup(Player playerIn) { + return false; + } + + @Override + public boolean mayPlace(@Nullable ItemStack stack) { + return false; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiAlignment.java b/src/main/java_old/se/mickelus/mutil/gui/GuiAlignment.java new file mode 100644 index 0000000..4950dd0 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiAlignment.java @@ -0,0 +1,25 @@ +package se.mickelus.mutil.gui; + +public enum GuiAlignment { + left, + center, + right; + + public GuiAlignment flip() { + if (this == left) { + return right; + } else if (this == right) { + return left; + } + return center; + } + + public GuiAttachment toAttachment() { + if (this == left) { + return GuiAttachment.topLeft; + } else if (this == right) { + return GuiAttachment.topRight; + } + return GuiAttachment.topCenter; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiAttachment.java b/src/main/java_old/se/mickelus/mutil/gui/GuiAttachment.java new file mode 100644 index 0000000..8edc9d6 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiAttachment.java @@ -0,0 +1,32 @@ +package se.mickelus.mutil.gui; + +public enum GuiAttachment { + topLeft, + topCenter, + topRight, + middleLeft, + middleCenter, + middleRight, + bottomLeft, + bottomCenter, + bottomRight; + + public GuiAttachment flipHorizontal() { + switch(this) { + case topLeft: + return topRight; + case topRight: + return topLeft; + case middleLeft: + return middleRight; + case middleRight: + return middleLeft; + case bottomLeft: + return bottomRight; + case bottomRight: + return bottomLeft; + default: + return this; + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiButton.java b/src/main/java_old/se/mickelus/mutil/gui/GuiButton.java new file mode 100644 index 0000000..9a90961 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiButton.java @@ -0,0 +1,75 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import se.mickelus.mutil.gui.impl.GuiColors; + +import java.util.Collections; +import java.util.List; + +public class GuiButton extends GuiClickable { + private final GuiStringOutline textElement; + + private boolean enabled = true; + private Component disabledTooltip; + + public GuiButton(int x, int y, int width, int height, String text, Runnable onClick) { + super(x, y, width, height, onClick); + + textElement = new GuiStringOutline(0, (height - 8) / 2, text); + addChild(textElement); + } + + public GuiButton(int x, int y, String text, Runnable onClick) { + this(x, y, Minecraft.getInstance().font.width(text), 10, text, onClick); + } + + public GuiButton(int x, int y, int width, int height, String text, Runnable onClick, Component disabledTooltip) { + this(x, y, width, height, text, onClick); + + this.disabledTooltip = disabledTooltip; + } + + @Override + public boolean onMouseClick(int x, int y, int button) { + return enabled && super.onMouseClick(x, y, button); + } + + private void updateColor() { + if (!enabled) { + textElement.setColor(GuiColors.muted); + } else if (hasFocus()) { + textElement.setColor(GuiColors.hover); + } else { + textElement.setColor(GuiColors.normal); + } + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + updateColor(); + } + + public void setText(String text) { + textElement.setString(text); + setWidth(Minecraft.getInstance().font.width(text)); + } + + @Override + protected void onFocus() { + updateColor(); + } + + @Override + protected void onBlur() { + updateColor(); + } + + @Override + public List getTooltipLines() { + if (!enabled && disabledTooltip != null && hasFocus()) { + return Collections.singletonList(disabledTooltip); + } + return null; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiClickable.java b/src/main/java_old/se/mickelus/mutil/gui/GuiClickable.java new file mode 100644 index 0000000..e91631e --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiClickable.java @@ -0,0 +1,29 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; + +public class GuiClickable extends GuiElement { + + protected final Runnable onClickHandler; + + public GuiClickable(int x, int y, int width, int height, Runnable onClickHandler) { + super(x, y, width, height); + + this.onClickHandler = onClickHandler; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + super.draw(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + } + + @Override + public boolean onMouseClick(int x, int y, int button) { + if (hasFocus()) { + onClickHandler.run(); + return true; + } + + return false; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiElement.java b/src/main/java_old/se/mickelus/mutil/gui/GuiElement.java new file mode 100644 index 0000000..f04a022 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiElement.java @@ -0,0 +1,386 @@ +package se.mickelus.mutil.gui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import se.mickelus.mutil.gui.animation.KeyframeAnimation; + +// todo 1.20: GuiComponent became GuiGraphics extension no longer makes sense, still works? +public class GuiElement { + protected int x; + protected int y; + protected GuiAttachment attachmentPoint = GuiAttachment.topLeft; + protected GuiAttachment attachmentAnchor = GuiAttachment.topLeft; + + protected int width; + protected int height; + + protected float opacity = 1; + + protected boolean hasFocus = false; + + protected boolean isVisible = true; + + protected boolean shouldRemove = false; + + protected ArrayList elements; + + protected Set activeAnimations; + + public GuiElement(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + elements = new ArrayList<>(); + + activeAnimations = new HashSet<>(); + } + + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, + float opacity) { + drawChildren(graphics, refX + x, refY + y, screenWidth, screenHeight, mouseX, mouseY, opacity * this.opacity); + } + + public void updateAnimations() { + // activeAnimations.stream() + // .filter(animation -> !animation.isActive()) + // .forEach(KeyframeAnimation::stop); + activeAnimations.removeIf(animation -> !animation.isActive()); + activeAnimations.forEach(KeyframeAnimation::preDraw); + } + + protected void drawChildren(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, + float opacity) { + elements.removeIf(GuiElement::shouldRemove); + elements.stream() + .filter(GuiElement::isVisible) + .forEach((element -> { + element.updateAnimations(); + element.draw( + graphics, refX + getXOffset(this, element.attachmentAnchor) - getXOffset(element, element.attachmentPoint), + refY + getYOffset(this, element.attachmentAnchor) - getYOffset(element, element.attachmentPoint), + screenWidth, screenHeight, mouseX, mouseY, opacity); + })); + } + + protected static int getXOffset(GuiElement element, GuiAttachment attachment) { + switch (attachment) { + case topLeft: + case middleLeft: + case bottomLeft: + return 0; + case topCenter: + case middleCenter: + case bottomCenter: + return element.getWidth() / 2; + case topRight: + case middleRight: + case bottomRight: + return element.getWidth(); + } + return 0; + } + + protected static int getYOffset(GuiElement element, GuiAttachment attachment) { + switch (attachment) { + case topLeft: + case topCenter: + case topRight: + return 0; + case middleLeft: + case middleCenter: + case middleRight: + return element.getHeight() / 2; + case bottomCenter: + case bottomLeft: + case bottomRight: + return element.getHeight(); + } + return 0; + } + + public boolean onMouseClick(int x, int y, int button) { + for (int i = elements.size() - 1; i >= 0; i--) { + if (elements.get(i).isVisible()) { + if (elements.get(i).onMouseClick(x, y, button)) { + return true; + } + } + } + + return false; + } + + public void onMouseRelease(int x, int y, int button) { + elements.forEach(element -> element.onMouseRelease(x, y, button)); + } + + public boolean onMouseScroll(double mouseX, double mouseY, double distance) { + for (int i = elements.size() - 1; i >= 0; i--) { + if (elements.get(i).isVisible()) { + if (elements.get(i).onMouseScroll(mouseX, mouseY, distance)) { + return true; + } + } + } + + return false; + } + + public boolean onKeyPress(int keyCode, int scanCode, int modifiers) { + for (int i = elements.size() - 1; i >= 0; i--) { + if (elements.get(i).isVisible()) { + if (elements.get(i).onKeyPress(keyCode, scanCode, modifiers)) { + return true; + } + } + } + + return false; + } + + public boolean onKeyRelease(int keyCode, int scanCode, int modifiers) { + for (int i = elements.size() - 1; i >= 0; i--) { + if (elements.get(i).isVisible()) { + if (elements.get(i).onKeyRelease(keyCode, scanCode, modifiers)) { + return true; + } + } + } + + return false; + } + + public boolean onCharType(char character, int modifiers) { + for (int i = elements.size() - 1; i >= 0; i--) { + if (elements.get(i).isVisible()) { + if (elements.get(i).onCharType(character, modifiers)) { + return true; + } + } + } + + return false; + } + + public void updateFocusState(int refX, int refY, int mouseX, int mouseY) { + elements.stream() + .filter(GuiElement::isVisible) + .forEach(element -> element.updateFocusState( + refX + x + getXOffset(this, element.attachmentAnchor) - getXOffset(element, element.attachmentPoint), + refY + y + getYOffset(this, element.attachmentAnchor) - getYOffset(element, element.attachmentPoint), + mouseX, mouseY)); + + boolean gainFocus = mouseX >= getX() + refX + && mouseX < getX() + refX + getWidth() + && mouseY >= getY() + refY + && mouseY < getY() + refY + getHeight(); + + if (gainFocus != hasFocus) { + hasFocus = gainFocus; + if (hasFocus) { + onFocus(); + } + else { + onBlur(); + } + } + } + + protected void onFocus() { + + } + + protected void onBlur() { + + } + + public boolean hasFocus() { + return hasFocus; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + /** + * Set which point, relative this element, that it should be positioned on. + */ + public GuiElement setAttachmentPoint(GuiAttachment attachment) { + attachmentPoint = attachment; + + return this; + } + + /** + * Set which point, relative the parent, that this element should be positioned on. + */ + public GuiElement setAttachmentAnchor(GuiAttachment attachment) { + attachmentAnchor = attachment; + + return this; + } + + public GuiElement setAttachment(GuiAttachment attachment) { + attachmentPoint = attachment; + attachmentAnchor = attachment; + + return this; + } + + public GuiAttachment getAttachmentPoint() { + return attachmentPoint; + } + + public GuiAttachment getAttachmentAnchor() { + return attachmentAnchor; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setVisible(boolean visible) { + if (isVisible != visible) { + if (visible) { + onShow(); + } + else { + if (!onHide()) { + return; + } + } + isVisible = visible; + } + } + + public boolean isVisible() { + return isVisible; + } + + protected void onShow() { + } + + /** + * Can be overridden to do something when the element is hidden. Returning false indicates that the handler will + * take care of setting isVisible to false. + */ + protected boolean onHide() { + this.hasFocus = false; + return true; + } + + public GuiElement setOpacity(float opacity) { + this.opacity = opacity; + return this; + } + + public float getOpacity() { + return opacity; + } + + public void addAnimation(KeyframeAnimation animation) { + activeAnimations.add(animation); + } + + public void removeAnimation(KeyframeAnimation animation) { + activeAnimations.remove(animation); + } + + public void remove() { + shouldRemove = true; + } + + public boolean shouldRemove() { + return shouldRemove; + } + + public void addChild(GuiElement child) { + this.elements.add(child); + } + + public void clearChildren() { + this.elements.clear(); + } + + public int getNumChildren() { + return elements.size(); + } + + public GuiElement getChild(int index) { + if (index >= 0 && index < elements.size()) { + return elements.get(index); + } + return null; + } + + public List getChildren() { + return Collections.unmodifiableList(elements); + } + + /** + * Return child elements which has the given type + */ + public List getChildren(Class type) { + return elements.stream() + .filter(type::isInstance) + .map(type::cast) + .collect(Collectors.toList()); + } + + public List getTooltipLines() { + if (isVisible()) { + return elements.stream() + .map(GuiElement::getTooltipLines) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + return null; + } + + protected static void drawRect(final GuiGraphics graphics, int left, int top, int right, int bottom, int color, float opacity) { + graphics.fill(left, top, right, bottom, colorWithOpacity(color, opacity)); + } + + protected static int colorWithOpacity(int color, float opacity) { + return colorWithOpacity(color, Math.round(opacity * 255)); + } + + protected static int colorWithOpacity(int color, int opacity) { + // replace alpha bits with passed opacity value, multiples opacity with current alpha bits if they are present + return color & 0xffffff | (opacity * (color >> 24 == 0 ? 255 : color >> 24 & 255) / 255 << 24); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiItem.java b/src/main/java_old/se/mickelus/mutil/gui/GuiItem.java new file mode 100644 index 0000000..05d3354 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiItem.java @@ -0,0 +1,125 @@ +package se.mickelus.mutil.gui; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.ArrayList; +import java.util.List; + +public class GuiItem extends GuiElement { + private Minecraft mc; + + private ItemStack itemStack; + + private boolean showTooltip = true; + private CountMode countMode = CountMode.normal; + + private float opacityThreshold = 1; + private boolean resetDepthTest = true; + + private boolean renderDecoration = true; + + public GuiItem(int x, int y) { + super(x, y, 16, 16); + + mc = Minecraft.getInstance(); + + setVisible(false); + } + + /** + * Sets the opacity threshold for this element, the item will only render when the combined opacity of this element and it's parent is above the + * threshold. + * + * @param opacityThreshold + * @return + */ + public GuiItem setOpacityThreshold(float opacityThreshold) { + this.opacityThreshold = opacityThreshold; + return this; + } + + public GuiItem setTooltip(boolean showTooltip) { + this.showTooltip = showTooltip; + return this; + } + + public GuiItem setCountVisibility(CountMode mode) { + this.countMode = mode; + return this; + } + + public GuiItem setItem(ItemStack itemStack) { + this.itemStack = itemStack; + setVisible(itemStack != null); + + return this; + } + + public GuiItem setResetDepthTest(boolean shouldReset) { + this.resetDepthTest = shouldReset; + return this; + } + + public GuiItem setRenderDecoration(boolean shouldRender) { + this.renderDecoration = shouldRender; + return this; + } + + // todo 1.20: blitOffset gone, still works? + // todo 1.20: how to render decorations? + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + super.draw(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + if (opacity * getOpacity() >= opacityThreshold) { + RenderSystem.applyModelViewMatrix(); + RenderSystem.enableDepthTest(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, + GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + graphics.renderItem(itemStack, refX + x, refY + y); + + if (renderDecoration) { + + } + + if (resetDepthTest) { + RenderSystem.disableDepthTest(); + } + } + } + + protected String getCountString() { + switch (countMode) { + case normal: + return null; + case always: + return String.valueOf(itemStack.getCount()); + case never: + return ""; + } + + return null; + } + + @Override + public List getTooltipLines() { + if (showTooltip && itemStack != null && hasFocus()) { + return new ArrayList<>(itemStack.getTooltipLines(Minecraft.getInstance().player, + mc.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL)); + } + + return null; + } + + public enum CountMode { + normal, // shows if count is > 1 + + always, + never + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiRect.java b/src/main/java_old/se/mickelus/mutil/gui/GuiRect.java new file mode 100644 index 0000000..a215c28 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiRect.java @@ -0,0 +1,40 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; + +public class GuiRect extends GuiElement { + + private int color; + private final boolean offset; + + public GuiRect(int x, int y, int width, int height, int color) { + this(x, y, width, height, color, false); + } + + public GuiRect(int x, int y, int width, int height, int color, boolean offset) { + super(x, y, offset ? width + 1 : width, offset ? height + 1 : height); + + this.color = color; + this.offset = offset; + } + + public GuiRect setColor(int color) { + this.color = color; + return this; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, + float opacity) { + super.draw(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + if (offset) { + graphics.pose().pushPose(); + graphics.pose().translate(0.5F, 0.5F, 0); + drawRect(graphics, refX + x, refY + y, refX + x + width - 1, refY + y + height - 1, color, opacity * getOpacity()); + graphics.pose().popPose(); + } + else { + drawRect(graphics, refX + x, refY + y, refX + x + width, refY + y + height, color, opacity * getOpacity()); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiRoot.java b/src/main/java_old/se/mickelus/mutil/gui/GuiRoot.java new file mode 100644 index 0000000..006b10c --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiRoot.java @@ -0,0 +1,29 @@ +package se.mickelus.mutil.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.platform.Window; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; + +public class GuiRoot extends GuiElement { + + protected Minecraft mc; + + public GuiRoot(Minecraft mc) { + super(0, 0, 0 ,0); + this.mc = mc; + } + + public void draw(GuiGraphics graphics) { + if (isVisible()) { + Window window = mc.getWindow(); + + width = window.getGuiScaledWidth(); + height = window.getGuiScaledHeight(); + double mouseX = mc.mouseHandler.xpos() * width / window.getScreenWidth(); + double mouseY = mc.mouseHandler.ypos() * height / window.getScreenHeight(); + + drawChildren(graphics, 0, 0, width, height, (int) mouseX, (int) mouseY, 1); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiString.java b/src/main/java_old/se/mickelus/mutil/gui/GuiString.java new file mode 100644 index 0000000..85dd0f6 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiString.java @@ -0,0 +1,96 @@ +package se.mickelus.mutil.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.MultiBufferSource; +import com.mojang.blaze3d.vertex.Tesselator; +import se.mickelus.mutil.gui.animation.KeyframeAnimation; + +public class GuiString extends GuiElement { + + protected String string; + + protected Font fontRenderer; + + protected int color = 0xffffffff; + protected boolean drawShadow = true; + + protected boolean fixedWidth = false; + + public GuiString(int x, int y, String string) { + super(x, y, 0, 9); + + fontRenderer = Minecraft.getInstance().font; + + this.string = string; + width = fontRenderer.width(string); + } + + public GuiString(int x, int y, int width, String string) { + super(x, y, width, 9); + + fixedWidth = true; + + fontRenderer = Minecraft.getInstance().font; + + this.string = fontRenderer.plainSubstrByWidth(string, width); + } + + public GuiString(int x, int y, String string, GuiAttachment attachment) { + this(x, y, string); + + attachmentPoint = attachment; + } + + public GuiString(int x, int y, String string, int color) { + this(x, y, string); + + this.color = color; + } + + public GuiString(int x, int y, String string, int color, GuiAttachment attachment) { + this(x, y, string, attachment); + + this.color = color; + } + + public void setColor(int color) { + this.color = color; + } + + public void setString(String string) { + if (string != null && !string.equals(this.string)) { + if (fixedWidth) { + this.string = fontRenderer.plainSubstrByWidth(string, width); + } else { + this.string = string; + width = fontRenderer.width(string); + } + } + } + + public GuiString setShadow(boolean shadow) { + drawShadow = shadow; + return this; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + activeAnimations.removeIf(keyframeAnimation -> !keyframeAnimation.isActive()); + activeAnimations.forEach(KeyframeAnimation::preDraw); + RenderSystem.enableBlend(); + drawString(graphics, string, refX + x, refY + y, color, opacity * getOpacity(), drawShadow); + } + + protected void drawString(final GuiGraphics graphics, String text, int x, int y, int color, float opacity, boolean drawShadow) { + color = colorWithOpacity(color, opacity); + + // if the vanilla fontrender considers the color to be almost transparent (0xfc) it flips the opacity back to 1 + if ((color & -67108864) != 0) { + graphics.drawString(fontRenderer, text, x, y, color, drawShadow); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiStringOutline.java b/src/main/java_old/se/mickelus/mutil/gui/GuiStringOutline.java new file mode 100644 index 0000000..61db175 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiStringOutline.java @@ -0,0 +1,71 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.GuiGraphics; + +public class GuiStringOutline extends GuiString { + private String cleanString; + + public GuiStringOutline(int x, int y, String string) { + super(x, y, string); + drawShadow = false; + + cleanString = ChatFormatting.stripFormatting(this.string); + } + + public GuiStringOutline(int x, int y, int width, String string) { + super(x, y, width, string); + drawShadow = false; + + cleanString = ChatFormatting.stripFormatting(this.string); + } + + public GuiStringOutline(int x, int y, String string, GuiAttachment attachment) { + super(x, y, string, attachment); + drawShadow = false; + + cleanString = ChatFormatting.stripFormatting(this.string); + } + + public GuiStringOutline(int x, int y, String string, int color) { + super(x, y, string, color); + drawShadow = false; + + cleanString = ChatFormatting.stripFormatting(this.string); + } + + public GuiStringOutline(int x, int y, String string, int color, GuiAttachment attachment) { + super(x, y, string, color, attachment); + drawShadow = false; + + cleanString = ChatFormatting.stripFormatting(this.string); + } + + @Override + public void setString(String string) { + super.setString(string); + + cleanString = ChatFormatting.stripFormatting(this.string); + } + + @Override + protected void drawString(final GuiGraphics graphics, String text, int x, int y, int color, float opacity, boolean drawShadow) { + + graphics.pose().pushPose(); + super.drawString(graphics, cleanString, x - 1, y - 1, 0, opacity, false); + super.drawString(graphics, cleanString, x, y - 1, 0, opacity, false); + super.drawString(graphics, cleanString, x + 1, y - 1, 0, opacity, false); + + super.drawString(graphics, cleanString, x - 1, y + 1, 0, opacity, false); + super.drawString(graphics, cleanString, x, y + 1, 0, opacity, false); + super.drawString(graphics, cleanString, x + 1, y + 1, 0, opacity, false); + + super.drawString(graphics, cleanString, x + 1, y, 0, opacity, false); + super.drawString(graphics, cleanString, x - 1, y, 0, opacity, false); + + // magic offset to avoid z-fighting for in-world rendering + graphics.pose().translate(0, 0, 0.0020000000949949026D); + super.drawString(graphics, text, x, y, color, opacity, false); + graphics.pose().popPose(); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiStringSmall.java b/src/main/java_old/se/mickelus/mutil/gui/GuiStringSmall.java new file mode 100644 index 0000000..fc8f4d5 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiStringSmall.java @@ -0,0 +1,63 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; +import se.mickelus.mutil.gui.animation.KeyframeAnimation; + +public class GuiStringSmall extends GuiString { + + public GuiStringSmall(int x, int y, String string) { + super(x*2, y*2, string); + } + + public GuiStringSmall(int x, int y, String string, int color) { + super(x*2, y*2, string, color); + } + + public GuiStringSmall(int x, int y, String string, GuiAttachment attachment) { + super(x*2, y*2, string, attachment); + } + + public GuiStringSmall(int x, int y, String string, int color, GuiAttachment attachment) { + super(x*2, y*2, string, color, attachment); + } + + @Override + public void setX(int x) { + super.setX(x * 2); + } + + @Override + public void setY(int y) { + super.setY(y * 2); + } + + @Override + public int getX() { + return super.getX() / 2; + } + + @Override + public int getY() { + return super.getY() / 2; + } + + @Override + public int getWidth() { + return width / 2; + } + + @Override + public int getHeight() { + return height / 2; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + activeAnimations.removeIf(keyframeAnimation -> !keyframeAnimation.isActive()); + activeAnimations.forEach(KeyframeAnimation::preDraw); + graphics.pose().pushPose(); + graphics.pose().scale(.5f, .5f, .5f); + drawString(graphics, string, refX * 2 + x, refY * 2 + y, color, opacity * getOpacity(), drawShadow); + graphics.pose().popPose(); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiText.java b/src/main/java_old/se/mickelus/mutil/gui/GuiText.java new file mode 100644 index 0000000..98ae520 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiText.java @@ -0,0 +1,62 @@ +package se.mickelus.mutil.gui; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; +import net.minecraft.util.FormattedCharSequence; + +import java.util.List; +import org.joml.Matrix4f; + +public class GuiText extends GuiElement { + + Font fontRenderer; + + String string; + int color = 0xffffff; + + public GuiText(int x, int y, int width, String string) { + super(x, y, width ,0); + + fontRenderer = Minecraft.getInstance().font; + setString(string); + } + + public void setString(String string) { + this.string = string.replace("\\n", "\n"); + + height = fontRenderer.wordWrapHeight(this.string, width); + } + + public void setColor(int color) { + this.color = color; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + renderText(graphics, fontRenderer, string, refX + x, refY + y, width, color, opacity); + + super.draw(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + } + + protected static void renderText(GuiGraphics graphics, Font fontRenderer, String string, int x, int y, int width, int color, float opacity) { + // todo 1.20: font rendering changed, test that it works + List list = fontRenderer.split(Component.literal(string), width); + for(FormattedCharSequence line : list) { + graphics.drawString(fontRenderer, line, x, y, colorWithOpacity(color, opacity)); + y += 9; +// float lineX = (float) x; +// MultiBufferSource.BufferSource buffer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); +// if (fontRenderer.getBidiFlag()) { +// int i = fontRenderer.getStringWidth(fontRenderer.bidiReorder(line.)); +// lineX += (float)(width - i); +// } +// fontRenderer.drawInBatch(line, lineX, (float)y, colorWithOpacity(color, opacity), true, matrix, buffer, false, 0, 15728880); +// buffer.endBatch(); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiTextSmall.java b/src/main/java_old/se/mickelus/mutil/gui/GuiTextSmall.java new file mode 100644 index 0000000..3bfbe9b --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiTextSmall.java @@ -0,0 +1,26 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; + +public class GuiTextSmall extends GuiText { + + public GuiTextSmall(int x, int y, int width, String string) { + super(x, y, width , string); + } + + public void setString(String string) { + this.string = string.replace("\\n", "\n"); + + height = fontRenderer.wordWrapHeight(this.string, width * 2) / 2; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + graphics.pose().pushPose(); + graphics.pose().scale(.5f, .5f, .5f); + renderText(graphics, fontRenderer, string, (refX + x) * 2, (refY + y) * 2, width * 2, 0xffffff, opacity); + graphics.pose().popPose(); + + drawChildren(graphics, refX + x, refY + y, screenWidth, screenHeight, mouseX, mouseY, opacity * this.opacity); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiTexture.java b/src/main/java_old/se/mickelus/mutil/gui/GuiTexture.java new file mode 100644 index 0000000..ff8741f --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiTexture.java @@ -0,0 +1,80 @@ +package se.mickelus.mutil.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.resources.ResourceLocation; + +public class GuiTexture extends GuiElement { + + protected ResourceLocation textureLocation; + + protected int textureWidth = 256; + protected int textureHeight = 256; + protected int textureX; + protected int textureY; + + protected int color = 0xffffff; + private boolean useDefaultBlending = true; + + public GuiTexture(int x, int y, int width, int height, ResourceLocation textureLocation) { + this(x, y, width, height, 0, 0, textureLocation); + } + + public GuiTexture(int x, int y, int width, int height, int textureX, int textureY, ResourceLocation textureLocation) { + super(x, y, width, height); + + this.textureX = textureX; + this.textureY = textureY; + + this.textureLocation = textureLocation; + } + + public GuiTexture setTextureCoordinates(int x, int y) { + textureX = x; + textureY = y; + return this; + } + + public GuiTexture setColor(int color) { + this.color = color; + return this; + } + + public GuiTexture setSpriteSize(int width, int height) { + this.textureWidth = width; + this.textureHeight = height; + return this; + } + + public GuiTexture setUseDefaultBlending(boolean useDefault) { + this.useDefaultBlending = useDefault; + return this; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, + float opacity) { + super.draw(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + + drawTexture(graphics, textureLocation, refX + x, refY + y, width, height, textureX, textureY, color, getOpacity() * opacity); + } + + protected void drawTexture(final GuiGraphics graphics, ResourceLocation textureLocation, int x, int y, int width, int height, + int u, int v, int color, float opacity) { + // todo 1.20: needs cleanup, simplified rendering with simple params seems to cause flickering during animations, defaultBlendFunc not needed anymore? +// if (useDefaultBlending) { +// RenderSystem.defaultBlendFunc(); +// } + +// if (color != 0xffffff || opacity != 1) { +// if (opacity > 0) { + graphics.innerBlit(textureLocation, x, x + width, y, y + height, 0, + u * 1f / textureWidth, (u + width) * 1f / textureWidth, + v * 1f / textureHeight, (v + height) * 1f / textureHeight, + (color >> 16 & 255) / 255f, (color >> 8 & 255) / 255f, (color & 255) / 255f, opacity); +// } +// } else { +// graphics.blit(textureLocation, x, y, 0, u, v, width, height, textureWidth, textureHeight); +// } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/GuiTextureOffset.java b/src/main/java_old/se/mickelus/mutil/gui/GuiTextureOffset.java new file mode 100644 index 0000000..2563ac6 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/GuiTextureOffset.java @@ -0,0 +1,29 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.resources.ResourceLocation; + +/** + * Texture with half "pixel" offset + */ +public class GuiTextureOffset extends GuiTexture { + + public GuiTextureOffset(int x, int y, int width, int height, ResourceLocation textureLocation) { + super(x, y, width + 1, height + 1, textureLocation); + } + + public GuiTextureOffset(int x, int y, int width, int height, int textureX, int textureY, ResourceLocation textureLocation) { + super(x, y, width + 1, height + 1, textureX, textureY, textureLocation); + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + drawChildren(graphics, refX + x, refY + y, screenWidth, screenHeight, mouseX, mouseY, opacity * this.opacity); + + graphics.pose().pushPose(); + graphics.pose().translate(0.5F, 0.5F, 0); + drawTexture(graphics, textureLocation, refX + x, refY + y, width - 1, height - 1, textureX, textureY, color, + getOpacity() * opacity); + graphics.pose().popPose(); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/ScrollBarGui.java b/src/main/java_old/se/mickelus/mutil/gui/ScrollBarGui.java new file mode 100644 index 0000000..fdf3167 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/ScrollBarGui.java @@ -0,0 +1,49 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.client.gui.GuiGraphics; +import se.mickelus.mutil.gui.impl.GuiHorizontalScrollable; + +public class ScrollBarGui extends GuiElement { + private final GuiHorizontalScrollable scrollable; + + private final boolean unscrollableHidden; + + public ScrollBarGui(int x, int y, int width, int height, GuiHorizontalScrollable scrollable) { + this(x, y, width, height, scrollable, false); + } + + public ScrollBarGui(int x, int y, int width, int height, GuiHorizontalScrollable scrollable, boolean unscrollableHidden) { + super(x, y, width, height); + + this.scrollable = scrollable; + this.unscrollableHidden = unscrollableHidden; + } + + private boolean isActive() { + return !unscrollableHidden || scrollable.getOffsetMax() > 0; + } + + @Override + public void draw(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, + float opacity) { + if (isActive()) { + super.draw(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + + drawBackground(graphics, refX + x, refY + y); + int contentWidth = scrollable.getOffsetMax() + scrollable.getWidth(); + + int handleWidth = Math.max(3, (int) (scrollable.getWidth() * 1f / contentWidth * width) + 1); + int handleOffset = (int) (scrollable.getOffset() / contentWidth * width); + + drawHandle(graphics, refX + x + handleOffset, refY + y, handleWidth); + } + } + + protected void drawBackground(final GuiGraphics graphics, int x, int y) { + drawRect(graphics, x, y, x + width, y + height, 0xffffff, opacity * 0.2f); + } + + protected void drawHandle(final GuiGraphics graphics, int x, int y, int handleWidth) { + drawRect(graphics, x, y, x + handleWidth, y + height, 0xffffff, opacity * 0.7f); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/ToggleableSlot.java b/src/main/java_old/se/mickelus/mutil/gui/ToggleableSlot.java new file mode 100644 index 0000000..9ba4b4f --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/ToggleableSlot.java @@ -0,0 +1,40 @@ +package se.mickelus.mutil.gui; + +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.SlotItemHandler; + +import javax.annotation.Nullable; + +public class ToggleableSlot extends SlotItemHandler { + + private boolean isEnabled = true; + private int realX, realY; + + public ToggleableSlot(IItemHandler itemHandler, int index, int xPosition, int yPosition) { + super(itemHandler, index, xPosition, yPosition); + + realX = xPosition; + realY = yPosition; + } + + public void toggle(boolean enabled) { + isEnabled = enabled; + } + + @Override + public boolean isActive() { + return isEnabled; + } + + @Override + public boolean mayPickup(Player playerIn) { + return isEnabled; + } + + @Override + public boolean mayPlace(@Nullable ItemStack stack) { + return isEnabled; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/animation/AnimationChain.java b/src/main/java_old/se/mickelus/mutil/gui/animation/AnimationChain.java new file mode 100644 index 0000000..78be939 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/animation/AnimationChain.java @@ -0,0 +1,74 @@ +package se.mickelus.mutil.gui.animation; + +import java.util.function.Consumer; + +public class AnimationChain implements GuiAnimation { + + private final KeyframeAnimation[] animations; + private KeyframeAnimation activeAnimation; + + private boolean looping = false; + private Consumer stopHandler; + + private boolean isActive; + + public AnimationChain(KeyframeAnimation ... animations) { + this.animations = animations; + + for (int i = 0; i < animations.length; i++) { + final int index = i; + animations[i].onStop(isActive -> { + if (isActive) { + startNext(index); + } else if (stopHandler != null) { + stopHandler.accept(false); + } + }); + } + } + + public AnimationChain setLooping(boolean looping) { + this.looping = looping; + return this; + } + + public AnimationChain onStop(Consumer handler) { + stopHandler = handler; + return this; + } + + public void stop() { + isActive = false; + if (activeAnimation != null) { + activeAnimation.stop(); + } + } + + public void start() { + isActive = true; + activeAnimation = animations[0]; + activeAnimation.start(); + } + + private void startNext(int currentIndex) { + if (isActive) { + if (currentIndex + 1 >= animations.length) { + if (looping) { + activeAnimation = animations[0]; + } else { + if (stopHandler != null) { + stopHandler.accept(true); + } + activeAnimation = null; + isActive = false; + } + } else { + activeAnimation = animations[currentIndex + 1]; + } + + if (activeAnimation != null) { + activeAnimation.start(); + } + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/animation/Applier.java b/src/main/java_old/se/mickelus/mutil/gui/animation/Applier.java new file mode 100644 index 0000000..40dffbd --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/animation/Applier.java @@ -0,0 +1,213 @@ +package se.mickelus.mutil.gui.animation; + +import se.mickelus.mutil.gui.GuiElement; + +public abstract class Applier { + + protected GuiElement element; + + protected boolean relativeStart; + protected boolean relativeTarget; + protected float startOffset = 0; + protected float targetOffset = 0; + + + protected float startValue; + protected float targetValue; + + protected float currentValue; + + public Applier(float targetValue) { + this(0, targetValue, true, false); + } + + public Applier(float startValue, float targetValue) { + this(startValue, targetValue, false, false); + } + + public Applier(float startValue, float targetValue, boolean relativeStart, boolean relativeTarget) { + this.targetValue = targetValue; + this.startValue = startValue; + + this.relativeStart = relativeStart; + this.relativeTarget = relativeTarget; + + this.startOffset = startValue; + this.targetOffset = targetValue; + } + + public void setElement(GuiElement element) { + this.element = element; + } + + public void start(int duration) { + if (relativeStart) { + startValue = getRelativeValue() + startOffset; + } + + if (relativeTarget) { + targetValue = getRelativeValue() + targetOffset; + } + + currentValue = startValue; + } + + protected abstract float getRelativeValue(); + + public void preDraw(float progress) { + currentValue = startValue + progress * (targetValue - startValue); + } + + + public static class TranslateX extends Applier { + + public TranslateX(float targetValue) { + super(targetValue); + } + + public TranslateX(float startValue, float targetValue) { + super(startValue, targetValue); + } + + public TranslateX(float startValue, float targetValue, boolean relative) { + super(startValue, targetValue, relative, relative); + } + + public TranslateX(float startValue, float targetValue, boolean relativeStart, boolean relativeTarget) { + super(startValue, targetValue, relativeStart, relativeTarget); + } + + @Override + public void start(int duration) { + super.start(duration); + if (!relativeStart) { + element.setX((int) startValue); + } + } + + @Override + protected float getRelativeValue() { + return element.getX(); + } + + @Override + public void preDraw(float progress) { + super.preDraw(progress); + element.setX((int) currentValue); + } + } + + public static class TranslateY extends Applier { + + public TranslateY(float targetValue) { + super(targetValue); + } + + public TranslateY(float startValue, float targetValue) { + super(startValue, targetValue); + } + + public TranslateY(float startValue, float targetValue, boolean relative) { + super(startValue, targetValue, relative, relative); + } + + public TranslateY(float startValue, float targetValue, boolean relativeStart, boolean relativeTarget) { + super(startValue, targetValue, relativeStart, relativeTarget); + } + + @Override + public void start(int duration) { + super.start(duration); + if (!relativeStart) { + element.setY((int) startValue); + } + } + + @Override + protected float getRelativeValue() { + return element.getY(); + } + + @Override + public void preDraw(float progress) { + super.preDraw(progress); + element.setY((int) currentValue); + } + } + + public static class Opacity extends Applier { + + public Opacity(float targetValue) { + super(targetValue); + } + + public Opacity(float startValue, float targetValue) { + super(startValue, targetValue); + } + + public Opacity(float startValue, float targetValue, boolean relative) { + super(startValue, targetValue, relative, relative); + } + + public Opacity(float startValue, float targetValue, boolean relativeStart, boolean relativeTarget) { + super(startValue, targetValue, relativeStart, relativeTarget); + } + + @Override + public void start(int duration) { + super.start(duration); + if (!relativeStart) { + element.setOpacity((int) startValue); + } + } + + @Override + protected float getRelativeValue() { + return element.getOpacity(); + } + + @Override + public void preDraw(float progress) { + super.preDraw(progress); + element.setOpacity(currentValue); + } + } + + public static class Width extends Applier { + + public Width(float targetValue) { + super(targetValue); + } + + public Width(float startValue, float targetValue) { + super(startValue, targetValue); + } + + public Width(float startValue, float targetValue, boolean relative) { + super(startValue, targetValue, relative, relative); + } + + public Width(float startValue, float targetValue, boolean relativeStart, boolean relativeTarget) { + super(startValue, targetValue, relativeStart, relativeTarget); + } + + @Override + public void start(int duration) { + super.start(duration); + if (!relativeStart) { + element.setWidth((int) startValue); + } + } + + @Override + protected float getRelativeValue() { + return element.getWidth(); + } + + @Override + public void preDraw(float progress) { + super.preDraw(progress); + element.setWidth((int) currentValue); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/animation/GuiAnimation.java b/src/main/java_old/se/mickelus/mutil/gui/animation/GuiAnimation.java new file mode 100644 index 0000000..29d80ca --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/animation/GuiAnimation.java @@ -0,0 +1,6 @@ +package se.mickelus.mutil.gui.animation; + +public interface GuiAnimation { + public void stop(); + public void start(); +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/animation/KeyframeAnimation.java b/src/main/java_old/se/mickelus/mutil/gui/animation/KeyframeAnimation.java new file mode 100644 index 0000000..0a401a6 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/animation/KeyframeAnimation.java @@ -0,0 +1,76 @@ +package se.mickelus.mutil.gui.animation; + +import se.mickelus.mutil.gui.GuiElement; + +import java.util.Arrays; +import java.util.function.Consumer; + +public class KeyframeAnimation implements GuiAnimation { + + private final int duration; + private int delay = 0; + private final GuiElement element; + private Consumer handler; + + + private Applier[] appliers; + + private long startTime; + private boolean isActive = false; + + public KeyframeAnimation(int duration, GuiElement element) { + this.duration = duration; + this.element = element; + } + + public KeyframeAnimation applyTo(Applier... appliers) { + this.appliers = appliers; + Arrays.stream(this.appliers).forEach(applier -> applier.setElement(element)); + return this; + } + + public KeyframeAnimation withDelay(int delay) { + this.delay = delay; + return this; + } + + public KeyframeAnimation onStop(Consumer handler) { + this.handler = handler; + return this; + } + + public void start() { + startTime = System.currentTimeMillis(); + + Arrays.stream(this.appliers).forEach(applier -> applier.start(duration)); + + isActive = true; + element.addAnimation(this); + } + + public void stop() { + // todo: hacky + if (handler != null) { + handler.accept(!isActive); + } + isActive = false; + } + + public void preDraw() { + long currentTime = System.currentTimeMillis(); + if (startTime + delay < currentTime) { + if (startTime + delay + duration > currentTime) { + float progress = (currentTime - delay - startTime) * 1f / duration; + Arrays.stream(appliers).forEach(applier -> applier.preDraw(progress)); + } else { + Arrays.stream(appliers).forEach(applier -> applier.preDraw(1)); + isActive = false; + stop(); + } + } + } + + public boolean isActive() { + return isActive; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/animation/VisibilityFilter.java b/src/main/java_old/se/mickelus/mutil/gui/animation/VisibilityFilter.java new file mode 100644 index 0000000..eee17dc --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/animation/VisibilityFilter.java @@ -0,0 +1,54 @@ +package se.mickelus.mutil.gui.animation; + +public class VisibilityFilter { + + private final float OPACITY_STEP = 0.1f; + private final int DECREASE_DELAY = 100; + + private final float min; + private final float max; + + private float input; + private float output; + + private int delay = DECREASE_DELAY; + + public VisibilityFilter(float min, float max) { + this.min = min; + this.max = max; + } + + public float apply(float value) { + input = value; + updateOutput(); + + return output; + } + + public float get() { + return output; + } + + private void updateOutput() { + if (min < input && input < max) { + if (output + OPACITY_STEP < 1) { + output += OPACITY_STEP; + } else { + output = 1; + } + + delay = DECREASE_DELAY; + } else { + if (delay == 0) { + if (output - OPACITY_STEP > 0) { + output -= OPACITY_STEP; + } else { + output = 0; + } + } else { + delay--; + } + } + } + +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/hud/GuiRootHud.java b/src/main/java_old/se/mickelus/mutil/gui/hud/GuiRootHud.java new file mode 100644 index 0000000..c95fcfe --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/hud/GuiRootHud.java @@ -0,0 +1,117 @@ +package se.mickelus.mutil.gui.hud; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.AABB; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraft.world.phys.Vec3; +import se.mickelus.mutil.gui.GuiElement; +import se.mickelus.mutil.gui.animation.KeyframeAnimation; + +public class GuiRootHud extends GuiElement { + + public GuiRootHud() { + super(0, 0, 0, 0); + } + + public void draw(GuiGraphics graphics, Vec3 proj, BlockHitResult rayTrace, VoxelShape shape) { + BlockPos blockPos = rayTrace.getBlockPos(); + + Vec3 hitVec = rayTrace.getLocation(); + + draw(graphics, blockPos.getX() - proj.x, blockPos.getY() - proj.y, blockPos.getZ() - proj.z, + hitVec.x - blockPos.getX(), hitVec.y - blockPos.getY(), hitVec.z - blockPos.getZ(), + rayTrace.getDirection(), shape.bounds()); + } + + public void draw(GuiGraphics graphics, double x, double y, double z, double hitX, double hitY, double hitZ, Direction facing, + AABB boundingBox) { + activeAnimations.removeIf(keyframeAnimation -> !keyframeAnimation.isActive()); + activeAnimations.forEach(KeyframeAnimation::preDraw); + + graphics.pose().pushPose(); + graphics.pose().translate(x, y, z); + + int mouseX = 0; + int mouseY = 0; + + float size = 64; + + // magic number is the same used to offset the outline, stops textures from flickering + Vec3 magicOffset = Vec3.atLowerCornerOf(facing.getNormal()).scale(0.0020000000949949026D); + graphics.pose().translate(magicOffset.x(), magicOffset.y(), magicOffset.z()); + + switch (facing) { + case NORTH: + mouseX = (int) ( ( boundingBox.maxX - hitX ) * size ); + mouseY = (int) ( ( boundingBox.maxY - hitY ) * size ); + + width = (int) ((boundingBox.maxX - boundingBox.minX) * size); + height = (int) ((boundingBox.maxY - boundingBox.minY) * size); + + graphics.pose().translate(boundingBox.maxX, boundingBox.maxY, boundingBox.minZ); + graphics.pose().mulPose(Axis.YP.rotationDegrees(180)); + break; + case SOUTH: + mouseX = (int) ( ( hitX - boundingBox.minX ) * size ); + mouseY = (int) ( ( boundingBox.maxY - hitY ) * size ); + + width = (int) ((boundingBox.maxX - boundingBox.minX) * size); + height = (int) ((boundingBox.maxY - boundingBox.minY) * size); + + graphics.pose().translate(boundingBox.minX, boundingBox.maxY, boundingBox.maxZ); + break; + case EAST: + mouseX = (int) ( ( boundingBox.maxZ - hitZ ) * size ); + mouseY = (int) ( ( boundingBox.maxY - hitY ) * size ); + + width = (int) ((boundingBox.maxZ - boundingBox.minZ) * size); + height = (int) ((boundingBox.maxY - boundingBox.minY) * size); + + graphics.pose().translate(boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ); + graphics.pose().mulPose(Axis.YP.rotationDegrees(90)); + break; + case WEST: + mouseX = (int) ( ( hitZ - boundingBox.minZ ) * size ); + mouseY = (int) ( ( boundingBox.maxY - hitY ) * size ); + + width = (int) ((boundingBox.maxZ - boundingBox.minZ) * size); + height = (int) ((boundingBox.maxY - boundingBox.minY) * size); + + graphics.pose().translate(boundingBox.minX, boundingBox.maxY, boundingBox.minZ); + graphics.pose().mulPose(Axis.YP.rotationDegrees(-90)); + break; + case UP: + mouseX = (int) ( ( boundingBox.maxX - hitX ) * size ); + mouseY = (int) ( ( boundingBox.maxZ - hitZ ) * size ); + + width = (int) ((boundingBox.maxX - boundingBox.minX) * size); + height = (int) ((boundingBox.maxZ - boundingBox.minZ) * size); + + graphics.pose().translate(boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ); + graphics.pose().mulPose(Axis.XP.rotationDegrees(90)); + graphics.pose().scale(-1, 1, 1); + break; + case DOWN: + mouseX = (int) ( ( hitX - boundingBox.minX ) * size ); + mouseY = (int) ( ( boundingBox.maxZ - hitZ ) * size ); + + width = (int) ((boundingBox.maxX - boundingBox.minX) * size); + height = (int) ((boundingBox.maxZ - boundingBox.minZ) * size); + + graphics.pose().translate(boundingBox.minX, boundingBox.minY, boundingBox.maxZ); + graphics.pose().mulPose(Axis.XP.rotationDegrees(90)); + break; + } + + graphics.pose().scale(1 / size, -1 / size, 1 / size); + graphics.pose().translate(0.0D, 0, 0.02); + updateFocusState(0, 0, mouseX, mouseY); + drawChildren(graphics, 0, 0, width, height, mouseX, mouseY, 1); + graphics.pose().popPose(); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/impl/GuiColors.java b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiColors.java new file mode 100644 index 0000000..b298876 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiColors.java @@ -0,0 +1,20 @@ +package se.mickelus.mutil.gui.impl; + +public class GuiColors { + public static final int normal = 0xffffff; + public static final int add = 0xaaffaa; + public static final int remove = 0xffaaaa; + public static final int change = 0xaaaaff; + public static final int hover = 0xffffcc; + public static final int selected = 0xffff00; + + public static final int muted = 0x7f7f7f; + public static final int hoverMuted = 0x8f8f6f; + + + public static final int warning = 0xffff00; + public static final int negative = 0xff5555; + public static final int positive = 0x55ff55; + + public static final int separator = normal; +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/impl/GuiHorizontalLayoutGroup.java b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiHorizontalLayoutGroup.java new file mode 100644 index 0000000..6b1af12 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiHorizontalLayoutGroup.java @@ -0,0 +1,51 @@ +package se.mickelus.mutil.gui.impl; + +import net.minecraft.client.gui.GuiGraphics; +import se.mickelus.mutil.gui.GuiElement; + +public class GuiHorizontalLayoutGroup extends GuiElement { + private boolean needsLayout = false; + + private int spacing = 0; + + public GuiHorizontalLayoutGroup(int x, int y, int height, int spacing) { + super(x, y, 0, height); + + this.spacing = spacing; + } + + @Override + public void addChild(GuiElement child) { + super.addChild(child); + triggerLayout(); + } + + public void triggerLayout() { + needsLayout = true; + } + + public void forceLayout() { + layoutChildren(); + } + + private void layoutChildren() { + int offset = 0; + + for (GuiElement child : getChildren()) { + child.setX(offset); + offset += child.getWidth() + spacing; + } + + setWidth(offset - spacing); + + needsLayout = false; + } + + @Override + protected void drawChildren(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + if (needsLayout) { + layoutChildren(); + } + super.drawChildren(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/impl/GuiHorizontalScrollable.java b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiHorizontalScrollable.java new file mode 100644 index 0000000..38a2ccc --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiHorizontalScrollable.java @@ -0,0 +1,148 @@ +package se.mickelus.mutil.gui.impl; + +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.util.Mth; +import se.mickelus.mutil.gui.GuiElement; + +public class GuiHorizontalScrollable extends GuiElement { + private boolean dirty = false; + private double scrollOffset = 0; + private double scrollVelocity = 0; + + private boolean isGlobal = false; + + private int min; + private int max; + + private long lastDraw = System.currentTimeMillis(); + + public GuiHorizontalScrollable(int x, int y, int width, int height) { + super(x, y, width, height); + } + + /** + * Set this scrollable to react to scrolling anywhere in the UI, not just while the scrollable has focus. + * @param isGlobal + * @return + */ + public GuiHorizontalScrollable setGlobal(boolean isGlobal) { + this.isGlobal = isGlobal; + return this; + } + + public double getOffset() { + return scrollOffset; + } + + public void setOffset(double offset) { + scrollOffset = Mth.clamp(offset, min, max); + } + + public int getOffsetMax() { + return max; + } + + /** + * Call when child layout/sizes change to cause bounds to update on next scroll. + */ + public void markDirty() { + dirty = true; + } + + public void forceRefreshBounds() { + calculateBounds(); + } + + private void calculateBounds() { + int tempMax = 0; + this.min = 0; + for (GuiElement element: getChildren()) { + int x = getXOffset(this, element.getAttachmentAnchor()) - getXOffset(element, element.getAttachmentPoint()); + this.min = Math.min(x, this.min); + tempMax = Math.max(x + element.getWidth(), tempMax); + } + this.max = Math.max(tempMax - width, 0); + scrollOffset = Mth.clamp(scrollOffset, min, max); + + dirty = false; + } + + @Override + public boolean onMouseScroll(double mouseX, double mouseY, double distance) { + if (super.onMouseScroll(mouseX, mouseY, distance)) { + return true; + } + + if (isGlobal || hasFocus()) { + if (Math.signum(scrollVelocity) != Math.signum(-distance)) { + scrollVelocity = 0; + } + + scrollVelocity -= distance * 12; + scrollOffset = Mth.clamp(scrollOffset - distance * 6, min, max); +// Minecraft.getInstance().getSoundHandler().play(SimpleSound.master(SoundEvents.UI_STONECUTTER_SELECT_RECIPE, (float) (0.5f + (scrollOffset - min) / max), 0.3f)); + + return true; + } + return false; + } + + @Override + public void updateFocusState(int refX, int refY, int mouseX, int mouseY) { + elements.stream() + .filter(GuiElement::isVisible) + .forEach(element -> element.updateFocusState( + refX + x + getXOffset(this, element.getAttachmentAnchor()) - getXOffset(element, element.getAttachmentPoint()) - (int) scrollOffset, + refY + y + getYOffset(this, element.getAttachmentAnchor()) - getYOffset(element, element.getAttachmentPoint()), + mouseX, mouseY)); + + boolean gainFocus = mouseX >= getX() + refX + && mouseX < getX() + refX + getWidth() + && mouseY >= getY() + refY + && mouseY < getY() + refY + getHeight(); + + if (gainFocus != hasFocus) { + hasFocus = gainFocus; + if (hasFocus) { + onFocus(); + } else { + onBlur(); + } + } + } + + @Override + protected void drawChildren(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + long now = System.currentTimeMillis(); + if (scrollVelocity != 0) { + double dist = (scrollVelocity * 0.2 + Math.signum(scrollVelocity) * 1) * (now - lastDraw) / 1000 * 50; + if (Math.signum(scrollVelocity) != Math.signum(scrollVelocity - dist)) { + dist = scrollVelocity; + scrollVelocity = 0; + } else { + scrollVelocity -= dist; + } + + scrollOffset = Mth.clamp(scrollOffset + dist, min, max); + } + + lastDraw = now; + super.drawChildren(graphics, refX - (int) scrollOffset, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + + if (dirty) { + calculateBounds(); + } + } + + @Override + public void addChild(GuiElement child) { + super.addChild(child); + markDirty(); + } + + @Override + public void clearChildren() { + super.clearChildren(); + markDirty(); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/gui/impl/GuiVerticalLayoutGroup.java b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiVerticalLayoutGroup.java new file mode 100644 index 0000000..4aedcbc --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/gui/impl/GuiVerticalLayoutGroup.java @@ -0,0 +1,51 @@ +package se.mickelus.mutil.gui.impl; + +import net.minecraft.client.gui.GuiGraphics; +import se.mickelus.mutil.gui.GuiElement; + +public class GuiVerticalLayoutGroup extends GuiElement { + private boolean needsLayout = false; + + private int spacing; + + public GuiVerticalLayoutGroup(int x, int y, int width, int spacing) { + super(x, y, 0, width); + + this.spacing = spacing; + } + + @Override + public void addChild(GuiElement child) { + super.addChild(child); + triggerLayout(); + } + + public void triggerLayout() { + needsLayout = true; + } + + public void forceLayout() { + layoutChildren(); + } + + private void layoutChildren() { + int offset = 0; + + for (GuiElement child : getChildren()) { + child.setY(offset); + offset += child.getHeight() + spacing; + } + + setHeight(offset - spacing); + + needsLayout = false; + } + + @Override + protected void drawChildren(final GuiGraphics graphics, int refX, int refY, int screenWidth, int screenHeight, int mouseX, int mouseY, float opacity) { + if (needsLayout) { + layoutChildren(); + } + super.drawChildren(graphics, refX, refY, screenWidth, screenHeight, mouseX, mouseY, opacity); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/network/AbstractPacket.java b/src/main/java_old/se/mickelus/mutil/network/AbstractPacket.java new file mode 100644 index 0000000..e4196ec --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/network/AbstractPacket.java @@ -0,0 +1,60 @@ +package se.mickelus.mutil.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Player; + +import java.io.IOException; + + +/** + * AbstractPacket class. Should be the parent of all packets wishing to use the PacketHandler. + * @author sirgingalot, mickelus + */ +public abstract class AbstractPacket { + + /** + * Encode the packet data into the ByteBuf stream. Complex data sets may need specific data handlers (See @link{cpw.mods.fml.common.network.ByteBuffUtils}) + * + * @param buffer the buffer to encode into + */ + public abstract void toBytes(FriendlyByteBuf buffer); + + /** + * Decode the packet data from the ByteBuf stream. Complex data sets may need specific data handlers (See @link{cpw.mods.fml.common.network.ByteBuffUtils}) + * + * @param buffer the buffer to decode from + */ + public abstract void fromBytes(FriendlyByteBuf buffer); + + /** + * Handle the reception of this packet. + * + * @param player A reference to the sending player when handled on the server side + */ + public abstract void handle(Player player); + + /** + * Utility method that reads a string from a buffer object. + * @param buffer The buffer containing the string to be read. + * @return A string read from the buffer + * @throws IOException + */ + protected static String readString(FriendlyByteBuf buffer) throws IOException { + String string = ""; + char c = buffer.readChar(); + + while(c != '\0') { + string += c; + c = buffer.readChar(); + } + + return string; + } + + protected static void writeString(String string, FriendlyByteBuf buffer) throws IOException { + for (int i = 0; i < string.length(); i++) { + buffer.writeChar(string.charAt(i)); + } + buffer.writeChar('\0'); + } +} \ No newline at end of file diff --git a/src/main/java_old/se/mickelus/mutil/network/BlockPosPacket.java b/src/main/java_old/se/mickelus/mutil/network/BlockPosPacket.java new file mode 100644 index 0000000..9810e4a --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/network/BlockPosPacket.java @@ -0,0 +1,30 @@ +package se.mickelus.mutil.network; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; + +public abstract class BlockPosPacket extends AbstractPacket { + + protected BlockPos pos; + + public BlockPosPacket() {} + + public BlockPosPacket(BlockPos pos) { + this.pos = pos; + } + + @Override + public void toBytes(FriendlyByteBuf buffer) { + buffer.writeInt(pos.getX()); + buffer.writeInt(pos.getY()); + buffer.writeInt(pos.getZ()); + } + + @Override + public void fromBytes(FriendlyByteBuf buffer) { + int x = buffer.readInt(); + int y = buffer.readInt(); + int z = buffer.readInt(); + pos = new BlockPos(x, y, z); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/network/PacketHandler.java b/src/main/java_old/se/mickelus/mutil/network/PacketHandler.java new file mode 100644 index 0000000..0903587 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/network/PacketHandler.java @@ -0,0 +1,107 @@ +package se.mickelus.mutil.network; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.network.NetworkDirection; +import net.minecraftforge.network.NetworkEvent; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.PacketDistributor; +import net.minecraftforge.network.simple.SimpleChannel; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.function.Supplier; + +@ParametersAreNonnullByDefault +public class PacketHandler { + private static final Logger logger = LogManager.getLogger(); + + private final SimpleChannel channel; + private final ArrayList> packets = new ArrayList<>(); + + public PacketHandler(String namespace, String channelId, String protocolVersion) { + channel = NetworkRegistry.newSimpleChannel( + new ResourceLocation(namespace, channelId), + () -> protocolVersion, + protocolVersion::equals, + protocolVersion::equals); + } + + /** + * Register your packet with the pipeline. Discriminators are automatically set. + * + * @param packetClass the class to register + * @param supplier A supplier returning an object instance of packetClass + * + * @return whether registration was successful. Failure may occur if 256 packets have been registered or if the registry already contains this packet + */ + public boolean registerPacket(Class packetClass, Supplier supplier) { + if (packets.size() > 256) { + logger.warn("Attempted to register packet but packet list is full: " + packetClass.toString()); + return false; + } + + if (packets.contains(packetClass)) { + logger.warn("Attempted to register packet but packet is already in list: " + packetClass.toString()); + return false; + } + + channel.messageBuilder(packetClass, packets.size()) + .encoder(AbstractPacket::toBytes) + .decoder(buffer -> { + T packet = supplier.get(); + packet.fromBytes(buffer); + return packet; + }) + .consumerNetworkThread(this::onMessage) + .add(); + + packets.add(packetClass); + return true; + } + + public void onMessage(AbstractPacket message, Supplier ctx) { + ctx.get().enqueueWork(() -> { + if (ctx.get().getDirection().getReceptionSide().isServer()) { + message.handle(ctx.get().getSender()); + } else { + message.handle(getClientPlayer()); + } + }); + ctx.get().setPacketHandled(true); + } + + @OnlyIn(Dist.CLIENT) + private Player getClientPlayer() { + return Minecraft.getInstance().player; + } + + public void sendTo(AbstractPacket message, ServerPlayer player) { + channel.sendTo(message, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } + + public void sendToAllPlayers(AbstractPacket message) { + channel.send(PacketDistributor.ALL.noArg(), message); + } + + public void sendToAllPlayersNear(AbstractPacket message, BlockPos pos, double r2, ResourceKey dim) { + channel.send(PacketDistributor.NEAR.with(PacketDistributor.TargetPoint.p(pos.getX(), pos.getY(), pos.getZ(), r2, dim)), message); + } + + @OnlyIn(Dist.CLIENT) + public void sendToServer(AbstractPacket message) { + // crashes sometimes happen due to the connection being null + if (Minecraft.getInstance().getConnection() != null) { + channel.sendToServer(message); + } + } +} \ No newline at end of file diff --git a/src/main/java_old/se/mickelus/mutil/scheduling/AbstractScheduler.java b/src/main/java_old/se/mickelus/mutil/scheduling/AbstractScheduler.java new file mode 100644 index 0000000..53eb3ac --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/scheduling/AbstractScheduler.java @@ -0,0 +1,54 @@ +package se.mickelus.mutil.scheduling; + +import com.google.common.collect.Queues; +import net.minecraft.server.TickTask; +import net.minecraftforge.event.TickEvent; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Iterator; +import java.util.Queue; + +@ParametersAreNonnullByDefault +public class AbstractScheduler { + private final Queue queue = Queues.newConcurrentLinkedQueue(); + private int counter; + + public void schedule(int delay, Runnable task) { + queue.add(new Task(counter + delay, task)); + } + + public void schedule(String id, int delay, Runnable task) { + queue.removeIf(t -> id.equals(t.id)); + queue.add(new Task(id, counter + delay, task)); + } + + public void tick(TickEvent event) { + if (event.phase != TickEvent.Phase.END) { + return; + } + + for (Iterator it = queue.iterator(); it.hasNext(); ) { + Task task = it.next(); + if (task.getTick() < counter) { + task.run(); + it.remove(); + } + } + + counter++; + } + + static class Task extends TickTask { + private String id; + + public Task(int timestamp, Runnable task) { + super(timestamp, task); + } + + public Task(String id, int timestamp, Runnable task) { + this(timestamp, task); + + this.id = id; + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/scheduling/ClientScheduler.java b/src/main/java_old/se/mickelus/mutil/scheduling/ClientScheduler.java new file mode 100644 index 0000000..2f756ee --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/scheduling/ClientScheduler.java @@ -0,0 +1,15 @@ +package se.mickelus.mutil.scheduling; + +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class ClientScheduler extends AbstractScheduler { + @SubscribeEvent + public void onClientTick(TickEvent.ClientTickEvent event) { + this.tick(event); + } + +} diff --git a/src/main/java_old/se/mickelus/mutil/scheduling/ServerScheduler.java b/src/main/java_old/se/mickelus/mutil/scheduling/ServerScheduler.java new file mode 100644 index 0000000..6b6343f --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/scheduling/ServerScheduler.java @@ -0,0 +1,14 @@ +package se.mickelus.mutil.scheduling; + +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class ServerScheduler extends AbstractScheduler { + @SubscribeEvent + public void onServerTick(TickEvent.ServerTickEvent event) { + tick(event); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/CastOptional.java b/src/main/java_old/se/mickelus/mutil/util/CastOptional.java new file mode 100644 index 0000000..5d24ad8 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/CastOptional.java @@ -0,0 +1,14 @@ +package se.mickelus.mutil.util; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Optional; + +@ParametersAreNonnullByDefault +public class CastOptional { + public static Optional cast(@Nullable Object object, Class clazz) { + return Optional.ofNullable(object) + .filter(clazz::isInstance) + .map(clazz::cast); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/Filter.java b/src/main/java_old/se/mickelus/mutil/util/Filter.java new file mode 100644 index 0000000..cfd12a5 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/Filter.java @@ -0,0 +1,22 @@ +package se.mickelus.mutil.util; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; + +@ParametersAreNonnullByDefault +public class Filter { + /** + * Filter a stream so that only one element for each "key" remains, the key is determined by the value returned + * by keyExtractor. + * @param keyExtractor + * @param + * @return + */ + public static Predicate distinct(Function keyExtractor) { + Set seen = ConcurrentHashMap.newKeySet(); + return t -> seen.add(keyExtractor.apply(t)); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/HexCodec.java b/src/main/java_old/se/mickelus/mutil/util/HexCodec.java new file mode 100644 index 0000000..a39acd0 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/HexCodec.java @@ -0,0 +1,29 @@ +package se.mickelus.mutil.util; + +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.codecs.PrimitiveCodec; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class HexCodec implements PrimitiveCodec { + public static final HexCodec instance = new HexCodec(); + + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getStringValue(input) + .map(val -> (int) Long.parseLong(val, 16)); + } + + @Override + public T write(final DynamicOps ops, final Integer value) { + return ops.createString(Integer.toHexString(value)); + } + + @Override + public String toString() { + return "mutil-hex"; + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/InventoryStream.java b/src/main/java_old/se/mickelus/mutil/util/InventoryStream.java new file mode 100644 index 0000000..d206933 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/InventoryStream.java @@ -0,0 +1,29 @@ +package se.mickelus.mutil.util; + +import net.minecraft.world.Container; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +@ParametersAreNonnullByDefault +public class InventoryStream { + public static Stream of(Container inventory) { + return StreamSupport.stream(new Spliterators.AbstractSpliterator(inventory.getContainerSize(), Spliterator.NONNULL | Spliterator.SIZED) { + int index = 0; + + public boolean tryAdvance(Consumer consumer) { + if (index < inventory.getContainerSize()) { + consumer.accept(inventory.getItem(index++)); + return true; + } else { + return false; + } + } + }, false); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/ItemHandlerWrapper.java b/src/main/java_old/se/mickelus/mutil/util/ItemHandlerWrapper.java new file mode 100644 index 0000000..f78045d --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/ItemHandlerWrapper.java @@ -0,0 +1,94 @@ +package se.mickelus.mutil.util; + +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.items.IItemHandler; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class ItemHandlerWrapper implements Container { + + protected final IItemHandler inv; + + public ItemHandlerWrapper(IItemHandler inv) { + this.inv = inv; + } + + /** + * Returns the size of this inventory. + */ + @Override + public int getContainerSize() { + return inv.getSlots(); + } + + /** + * Returns the stack in this slot. This stack should be a modifiable reference, not a copy of a stack in your inventory. + */ + @Override + public ItemStack getItem(int slot) { + return inv.getStackInSlot(slot); + } + + /** + * Attempts to remove n items from the specified slot. Returns the split stack that was removed. Modifies the inventory. + */ + @Override + public ItemStack removeItem(int slot, int count) { + ItemStack stack = inv.getStackInSlot(slot); + return stack.isEmpty() ? ItemStack.EMPTY : stack.split(count); + } + + /** + * Sets the contents of this slot to the provided stack. + */ + @Override + public void setItem(int slot, ItemStack stack) { + inv.insertItem(slot, stack, false); + } + + /** + * Removes the stack contained in this slot from the underlying handler, and returns it. + */ + @Override + public ItemStack removeItemNoUpdate(int index) { + ItemStack s = getItem(index); + if(s.isEmpty()) return ItemStack.EMPTY; + setItem(index, ItemStack.EMPTY); + return s; + } + + @Override + public boolean isEmpty() { + for(int i = 0; i < inv.getSlots(); i++) { + if(!inv.getStackInSlot(i).isEmpty()) return false; + } + return true; + } + + @Override + public boolean canPlaceItem(int slot, ItemStack stack) { + return inv.isItemValid(slot, stack); + } + + @Override + public void clearContent() { + for(int i = 0; i < inv.getSlots(); i++) { + inv.extractItem(i, 64, false); + } + } + + //The following methods are never used by vanilla in crafting. They are defunct as mods need not override them. + @Override + public int getMaxStackSize() { return 0; } + @Override + public void setChanged() {} + @Override + public boolean stillValid(Player player) { return false; } + @Override + public void startOpen(Player player) {} + @Override + public void stopOpen(Player player) {} +} diff --git a/src/main/java_old/se/mickelus/mutil/util/JsonOptional.java b/src/main/java_old/se/mickelus/mutil/util/JsonOptional.java new file mode 100644 index 0000000..c8c6331 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/JsonOptional.java @@ -0,0 +1,18 @@ +package se.mickelus.mutil.util; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Optional; + +@ParametersAreNonnullByDefault +public class JsonOptional { + public static Optional field(JsonObject object, String field) { + if (object.has(field)) { + return Optional.of(object.get(field)); + } + + return Optional.empty(); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/ParticleHelper.java b/src/main/java_old/se/mickelus/mutil/util/ParticleHelper.java new file mode 100644 index 0000000..0cb90ee --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/ParticleHelper.java @@ -0,0 +1,31 @@ +package se.mickelus.mutil.util; + +import net.minecraft.core.particles.ItemParticleOption; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class ParticleHelper { + public static void spawnArmorParticles(LivingEntity entity) { + spawnArmorParticles(entity, EquipmentSlot.values()[2 + entity.getRandom().nextInt(4)]); + } + + public static void spawnArmorParticles(LivingEntity entity, EquipmentSlot slot) { + RandomSource rand = entity.getRandom(); + ItemStack itemStack = entity.getItemBySlot(slot); + if (!itemStack.isEmpty()) { + ((ServerLevel) entity.level()).sendParticles(new ItemParticleOption(ParticleTypes.ITEM, itemStack), + entity.getX() + entity.getBbWidth() * (0.3 + rand.nextGaussian() * 0.4), + entity.getY() + entity.getBbHeight() * (0.2 + rand.nextGaussian() * 0.4), + entity.getZ() + entity.getBbWidth() * (0.3 + rand.nextGaussian() * 0.4), + 10, + 0, 0, 0, 0f); + } + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/RotationHelper.java b/src/main/java_old/se/mickelus/mutil/util/RotationHelper.java new file mode 100644 index 0000000..830471b --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/RotationHelper.java @@ -0,0 +1,93 @@ +package se.mickelus.mutil.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.util.Mth; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class RotationHelper { + public static Rotation rotationFromFacing(Direction facing) { + switch (facing) { + case UP: + case DOWN: + case NORTH: + return Rotation.NONE; + case SOUTH: + return Rotation.CLOCKWISE_180; + case EAST: + return Rotation.CLOCKWISE_90; + case WEST: + return Rotation.COUNTERCLOCKWISE_90; + default: + return Rotation.NONE; + } + } + + public static BlockPos rotatePitch(BlockPos pos, float pitch) { + float f = Mth.cos(pitch); + float f1 = Mth.sin(pitch); + float x = pos.getX(); + float y = pos.getY() * f + pos.getZ() * f1; + float z = pos.getZ() * f - pos.getY() * f1; + return new BlockPos(Math.round(x), Math.round(y), Math.round(z)); + } + + public static BlockPos rotateYaw(BlockPos pos, float yaw) { + float f = Mth.cos(yaw); + float f1 = Mth.sin(yaw); + double x = pos.getX() * (double)f + pos.getZ() * (double)f1; + double y = pos.getY(); + double z = pos.getZ() * (double)f - pos.getX() * (double)f1; + return new BlockPos((int) Math.round(x), (int) Math.round(y), (int) Math.round(z)); + } + + public static BlockPos rotateDirection(BlockPos pos, Direction facing) { + switch (facing) { + default: + case SOUTH: + return pos; + case WEST: + return new BlockPos(-pos.getZ(), pos.getY(), pos.getX()); + case NORTH: + return new BlockPos(-pos.getX(), pos.getY(), -pos.getZ()); + case EAST: + return new BlockPos(pos.getZ(), pos.getY(), -pos.getX()); + } + } + + // todo: there has to be a less hacky way? + public static VoxelShape rotateDirection(VoxelShape shape, Direction facing) { + VoxelShape[] temp = new VoxelShape[] { shape.move(-0.5, 0, -0.5), Shapes.empty() }; + + for (int i = 0; i < facing.get2DDataValue(); i++) { + temp[0].forAllBoxes((x1, y1, z1, x2, y2, z2) -> temp[1] = Shapes.or(temp[1], + Shapes.box(Math.min(-z1, -z2), y1, Math.min(x1, x2), + Math.max(-z1, -z2), y2, Math.max(x1, x2)))); + temp[0] = temp[1]; + temp[1] = Shapes.empty(); + } + + return temp[0].move(0.5, 0, 0.5); + } + + public static Vec3i shiftAxis(Vec3i pos) { + return new Vec3i(pos.getY(), pos.getZ(), pos.getX()); + } + + /** + * Returns the horizontal angle between two points, in radians (is that how you say it?) + * @param a + * @param b + * @return + */ + public static double getHorizontalAngle(Vec3 a, Vec3 b) { + return Mth.atan2(a.x - b.x, a.z - b.z); + } +} diff --git a/src/main/java_old/se/mickelus/mutil/util/TileEntityOptional.java b/src/main/java_old/se/mickelus/mutil/util/TileEntityOptional.java new file mode 100644 index 0000000..f004381 --- /dev/null +++ b/src/main/java_old/se/mickelus/mutil/util/TileEntityOptional.java @@ -0,0 +1,14 @@ +package se.mickelus.mutil.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Optional; + +@ParametersAreNonnullByDefault +public class TileEntityOptional { + public static Optional from(BlockGetter world, BlockPos pos, Class tileEntityClass) { + return CastOptional.cast(world.getBlockEntity(pos), tileEntityClass); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index f800864..82c6dff 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1 +1,2 @@ -public net.minecraft.client.gui.GuiGraphics m_280479_(Lnet/minecraft/resources/ResourceLocation;IIIIIFFFFFFFF)V # innerBlit \ No newline at end of file +#public net.minecraft.client.gui.GuiGraphics m_280479_(Lnet/minecraft/resources/ResourceLocation;IIIIIFFFFFFFF)V # innerBlit +public net.minecraft.client.gui.GuiGraphics innerBlit(Lnet/minecraft/resources/ResourceLocation;IIIIIFFFFFFFF)V \ No newline at end of file diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/neoforge.mods.toml similarity index 79% rename from src/main/resources/META-INF/mods.toml rename to src/main/resources/META-INF/neoforge.mods.toml index 3c988be..3f90a05 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -9,14 +9,14 @@ authors="${mod_authors}" description='''${mod_description}''' [[dependencies.${mod_id}]] - modId="forge" - mandatory=true - versionRange="${forge_version_range}" + modId="neoforge" + type="required" + versionRange="${neoforge_version_range}" ordering="NONE" side="BOTH" [[dependencies.${mod_id}]] modId="minecraft" - mandatory=true + type="required" versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 23c861b..7f0eb93 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { "description": "mutil resources", - "pack_format": 15 + "pack_format": 34 } }